diff --git a/Universal_Serial_Adapter/Config.cpp b/Universal_Serial_Adapter/Config.cpp index 5e4cfe7..9f9ebf9 100644 --- a/Universal_Serial_Adapter/Config.cpp +++ b/Universal_Serial_Adapter/Config.cpp @@ -16,7 +16,7 @@ Config::Config() { #if DEBUG == 2 - Serial.println("Config::Config()"); + serialPort0.println("Config::Config()"); #endif currentMode = none; @@ -29,48 +29,48 @@ Config::Config() { pinMode(voltagePinFivePointZero, OUTPUT); #if DEBUG == 2 - Serial.begin(115200); + serialPort0.begin(115200); #endif } bool Config::isUIEnabled() { #if DEBUG == 2 - Serial.println("Config::isUIEnabled()"); + serialPort0.println("Config::isUIEnabled()"); #endif return uiEnabled; } void Config::enableUI() { #if DEBUG == 2 - Serial.println("Config::enableUI()"); + serialPort0.println("Config::enableUI()"); #endif uiEnabled = true; } void Config::disableUI() { #if DEBUG == 2 - Serial.println("Config::disableUI()"); + serialPort0.println("Config::disableUI()"); #endif uiEnabled = false; } serialmode Config::getSerialMode() { #if DEBUG == 2 - Serial.println("Config::getSerialMode()"); + serialPort0.println("Config::getSerialMode()"); #endif return currentMode; } linespeed Config::getLineSpeed() { #if DEBUG == 2 - Serial.println("Config::getLineSpeed()"); + serialPort0.println("Config::getLineSpeed()"); #endif return currentLineSpeed; } float Config::getLineSpeedBaud() { #if DEBUG == 2 - Serial.println("Config::getLineSpeedBaud()"); + serialPort0.println("Config::getLineSpeedBaud()"); #endif switch (currentLineSpeed) { @@ -97,21 +97,21 @@ float Config::getLineSpeedBaud() { ttlvoltage Config::getVoltage() { #if DEBUG == 2 - Serial.println("Config::getVoltage()"); + serialPort0.println("Config::getVoltage()"); #endif return currentVoltage; } timeout Config::getTimeout() { #if DEBUG == 2 - Serial.println("Config::getTimeout()"); + serialPort0.println("Config::getTimeout()"); #endif return currentTimeout; } int Config::getTimeoutMilis() { #if DEBUG == 2 - Serial.println("Config::getTimeoutMilis()"); + serialPort0.println("Config::getTimeoutMilis()"); #endif switch (currentTimeout) { case 0: // tenseconds @@ -128,33 +128,33 @@ int Config::getTimeoutMilis() { void Config::setMode(serialmode mode) { #if DEBUG == 2 - Serial.println("Config::setMode()"); - Serial.print(" Setting ttl line speed to:"); + serialPort0.println("Config::setMode()"); + serialPort0.print(" Setting ttl line speed to:"); float baudrate = this->getLineSpeedBaud(); - Serial.println(baudrate); + serialPort0.println(baudrate); #endif switch (currentMode) { case 1: // ttl - Serial3.end(); + serialPort3.end(); break; case 2: // db9_null - Serial2.end(); + serialPort2.end(); break; case 3: // cisco - Serial1.end(); + serialPort1.end(); break; } switch (mode) { case 0: // ttl - Serial3.begin(getLineSpeedBaud()); + serialPort3.begin(getLineSpeedBaud()); break; case 1: // db9_null - Serial2.begin(getLineSpeedBaud()); + serialPort2.begin(getLineSpeedBaud()); break; case 2: // cisco - Serial1.begin(getLineSpeedBaud()); + serialPort1.begin(getLineSpeedBaud()); break; } @@ -163,17 +163,17 @@ void Config::setMode(serialmode mode) { void Config::setLineSpeed(linespeed aLineSpeed) { #if DEBUG == 2 - Serial.println("Config::setLineSpeed()"); + serialPort0.println("Config::setLineSpeed()"); #endif currentLineSpeed = aLineSpeed; - Serial.end(); - Serial.begin(getLineSpeedBaud()); + serialPort0.end(); + serialPort0.begin(getLineSpeedBaud()); setMode(currentMode); } void Config::setVoltage(ttlvoltage voltage) { #if DEBUG == 2 - Serial.println("Config::setVoltage()"); + serialPort0.println("Config::setVoltage()"); #endif currentVoltage = voltage; @@ -197,7 +197,7 @@ void Config::setVoltage(ttlvoltage voltage) { void Config::setLCDTimeout(timeout aTimeout) { #if DEBUG == 2 - Serial.println("Config::setTimeout()"); + serialPort0.println("Config::setTimeout()"); #endif currentTimeout = aTimeout; ui->setLCDTimeout(); @@ -205,7 +205,7 @@ void Config::setLCDTimeout(timeout aTimeout) { void Config::setDefaults() { #if DEBUG == 2 - Serial.println("Config::setDefaults()"); + serialPort0.println("Config::setDefaults()"); #endif setVoltage(onePointEight); setLineSpeed(oneFifteenTwoK); @@ -234,43 +234,43 @@ void Config::processSerialData() { //#endif switch (currentMode) { case 0: // ttl - if (Serial3.available()) { - int inByte = Serial3.read(); - Serial.write(inByte); + if (serialPort3.available()) { + int inByte = serialPort3.read(); + serialPort0.write(inByte); ui->blinkCancelButton(); dataFile.print(inByte); } - if (Serial.available()) { - int inByte = Serial.read(); - Serial3.write(inByte); + if (serialPort0.available()) { + int inByte = serialPort0.read(); + serialPort3.write(inByte); ui->blinkOKButton(); dataFile.print(inByte); } break; case 1: // db9_null - if (Serial2.available()) { - int inByte = Serial2.read(); - Serial.write(inByte); + if (serialPort2.available()) { + int inByte = serialPort2.read(); + serialPort0.write(inByte); ui->blinkCancelButton(); dataFile.print(inByte); } - if (Serial.available()) { - int inByte = Serial.read(); - Serial2.write(inByte); + if (serialPort0.available()) { + int inByte = serialPort0.read(); + serialPort2.write(inByte); ui->blinkOKButton(); dataFile.print(inByte); } break; case 2: // cisco - if (Serial1.available()) { - int inByte = Serial1.read(); - Serial.write(inByte); + if (serialPort1.available()) { + int inByte = serialPort1.read(); + serialPort0.write(inByte); ui->blinkCancelButton(); dataFile.print(inByte); } - if (Serial.available()) { - int inByte = Serial.read(); - Serial1.write(inByte); + if (serialPort0.available()) { + int inByte = serialPort0.read(); + serialPort1.write(inByte); ui->blinkOKButton(); dataFile.print(inByte); } @@ -278,5 +278,5 @@ void Config::processSerialData() { } // Flush data written to log file - dataFile.flush(); + dataFile.sync(); } diff --git a/Universal_Serial_Adapter/Config.h b/Universal_Serial_Adapter/Config.h index bc090fc..5d31358 100644 --- a/Universal_Serial_Adapter/Config.h +++ b/Universal_Serial_Adapter/Config.h @@ -13,7 +13,7 @@ #include "Project.h" #include "UI.h" -#include +#include // Forward declaration of UI class UI; @@ -22,7 +22,7 @@ class UI; extern UI* ui; // File to log data to -extern File dataFile; +extern SdFile dataFile; class Config { private: diff --git a/Universal_Serial_Adapter/Libraries/SdFat/ArduinoStream.h b/Universal_Serial_Adapter/Libraries/SdFat/ArduinoStream.h new file mode 100644 index 0000000..b3e64e0 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/ArduinoStream.h @@ -0,0 +1,119 @@ +/* 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 ArduinoStream_h +#define ArduinoStream_h +/** + * \file + * \brief ArduinoInStream and ArduinoOutStream classes + */ +#include +//============================================================================== +/** + * \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) { + hw_ = &hws; + line_ = buf; + size_ = size; + } + /** read a line. */ + void readline() { + size_t i = 0; + uint32_t t; + line_[0] = '\0'; + while (!hw_->available()); + + while (1) { + t = millis(); + while (!hw_->available()) { + if ((millis() - t) > 10) goto done; + } + if (i >= (size_ - 1)) { + setstate(failbit); + return; + } + line_[i++] = hw_->read(); + line_[i] = '\0'; + } + done: + init(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 *line_; + size_t size_; + Stream* 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) : pr_(&pr) {} + + protected: + /// @cond SHOW_PROTECTED + /** + * Internal do not use + * \param[in] c + */ + void putch(char c) { + if (c == '\n') pr_->write('\r'); + pr_->write(c); + } + void putstr(const char* str) {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* pr_; +}; +#endif // ArduinoStream_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/MinimumSerial.cpp b/Universal_Serial_Adapter/Libraries/SdFat/MinimumSerial.cpp new file mode 100644 index 0000000..9ae152d --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/MinimumSerial.cpp @@ -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 + * . + */ +#include +#if defined(UDR0) || defined(DOXYGEN) +#include +//------------------------------------------------------------------------------ +/** + * 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 MinimumSerial::begin(unsigned long baud) { + uint16_t baud_setting; + // don't worry, the compiler will squeeze out F_CPU != 16000000UL + if (F_CPU != 16000000UL || baud != 57600) { + // 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) ; +} +//------------------------------------------------------------------------------ +/** + * Unbuffered read + * \return -1 if no character is available or an available character. + */ +int MinimumSerial::read() { + if (UCSR0A & (1 << RXC0)) return UDR0; + return -1; +} +//------------------------------------------------------------------------------ +/** + * Unbuffered write + * + * \param[in] b byte to write. + * \return 1 + */ +size_t MinimumSerial::write(uint8_t b) { + while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} + UDR0 = b; + return 1; +} +MinimumSerial MiniSerial; +#endif // defined(UDR0) || defined(DOXYGEN) \ No newline at end of file diff --git a/Universal_Serial_Adapter/Libraries/SdFat/MinimumSerial.h b/Universal_Serial_Adapter/Libraries/SdFat/MinimumSerial.h new file mode 100644 index 0000000..e4a6e6c --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/MinimumSerial.h @@ -0,0 +1,36 @@ +/* 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 +/** + * \class MinimumSerial + * \brief mini serial class for the %SdFat library. + */ +class MinimumSerial : public Print { + public: + void begin(unsigned long); + int read(); + size_t write(uint8_t b); + using Print::write; +}; +#ifdef UDR0 +extern MinimumSerial MiniSerial; +#endif // UDR0 +#endif // MinimumSerial_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/Sd2Card.cpp b/Universal_Serial_Adapter/Libraries/SdFat/Sd2Card.cpp new file mode 100644 index 0000000..0754d1d --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/Sd2Card.cpp @@ -0,0 +1,1205 @@ +/* Arduino Sd2Card Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino Sd2Card Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino Sd2Card Library. If not, see + * . + */ +#include +// debug trace macro +#define SD_TRACE(m, b) +// #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); + +// SPI functions +//============================================================================== +#if USE_ARDUINO_SPI_LIBRARY +#include +//------------------------------------------------------------------------------ +static void spiBegin() { + SPI.begin(); +} +//------------------------------------------------------------------------------ +static void spiInit(uint8_t spiRate) { + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + int v; +#ifdef SPI_CLOCK_DIV128 + switch (spiRate/2) { + case 0: v = SPI_CLOCK_DIV2; break; + case 1: v = SPI_CLOCK_DIV4; break; + case 2: v = SPI_CLOCK_DIV8; break; + case 3: v = SPI_CLOCK_DIV16; break; + case 4: v = SPI_CLOCK_DIV32; break; + case 5: v = SPI_CLOCK_DIV64; break; + default: v = SPI_CLOCK_DIV128; break; + } +#else // SPI_CLOCK_DIV128 + if (spiRate > 13) { + v = 255; + } else { + v = (2 | (spiRate & 1)) << (spiRate/2); + } +#endif // SPI_CLOCK_DIV128 + SPI.setClockDivider(v); +} +//------------------------------------------------------------------------------ +/** SPI receive a byte */ +static uint8_t spiRec() { + return SPI.transfer(0XFF); +} +//------------------------------------------------------------------------------ +/** SPI receive multiple bytes */ +static uint8_t spiRec(uint8_t* buf, size_t len) { + for (size_t i = 0; i < len; i++) { + buf[i] = SPI.transfer(0XFF); + } + return 0; +} +//------------------------------------------------------------------------------ +/** SPI send a byte */ +static void spiSend(uint8_t b) { + SPI.transfer(b); +} +//------------------------------------------------------------------------------ +/** SPI send multiple bytes */ +static void spiSend(const uint8_t* buf, size_t len) { + for (size_t i = 0; i < len; i++) { + SPI.transfer(buf[i]); + } +} +//============================================================================== +#elif USE_NATIVE_SAM3X_SPI +/** 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; +} +//------------------------------------------------------------------------------ +static void spiBegin() { + 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 +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 +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 +static void spiInit(uint8_t spiRate) { + Spi* pSpi = SPI0; + uint8_t scbr = 255; + if (spiRate < 14) { + scbr = (2 | (spiRate & 1)) << (spiRate/2); + } + // 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 */ +static inline uint8_t spiRec() { + return spiTransfer(0XFF); +} +//------------------------------------------------------------------------------ +/** SPI receive multiple bytes */ +static uint8_t spiRec(uint8_t* buf, size_t len) { + Spi* pSpi = SPI0; + int rtn = 0; +#if USE_SAM3X_DMAC + // clear overrun error + uint32_t s = pSpi->SPI_SR; + + spiDmaRX(buf, len); + spiDmaTX(0, len); + + 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 < len; 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 */ +static inline void spiSend(uint8_t b) { + spiTransfer(b); +} +//------------------------------------------------------------------------------ +static void spiSend(const uint8_t* buf, size_t len) { + Spi* pSpi = SPI0; +#if USE_SAM3X_DMAC + spiDmaTX(buf, len); + 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 < len; 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; +} +//============================================================================== +#elif USE_NATIVE_MK20DX128_SPI +// Teensy 3.0 functions +#include +// 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 + */ +static void spiBegin() { + SIM_SCGC6 |= SIM_SCGC6_SPI0; +} +//------------------------------------------------------------------------------ +/** + * Initialize hardware SPI + * Set SCK rate to F_CPU/pow(2, 1 + spiRate) for spiRate [0,6] + */ +static void spiInit(uint8_t spiRate) { + // spiRate = 0 or 1 : 24 or 12 Mbit/sec + // spiRate = 2 or 3 : 12 or 6 Mbit/sec + // spiRate = 4 or 5 : 6 or 3 Mbit/sec + // spiRate = 6 or 7 : 3 or 1.5 Mbit/sec + // spiRate = 8 or 9 : 1.5 or 0.75 Mbit/sec + // spiRate = 10 or 11 : 250 kbit/sec + // spiRate = 12 or more : 125 kbit/sec + uint32_t ctar, ctar0, ctar1; + switch (spiRate/2) { + case 0: ctar = SPI_CTAR_DBR | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); break; + case 1: ctar = SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); break; + case 2: ctar = SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); break; + case 3: ctar = SPI_CTAR_BR(2) | SPI_CTAR_CSSCK(2); break; + case 4: ctar = SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(3); break; +#if F_BUS == 48000000 + case 5: ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(5); break; + default: ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(6); +#elif F_BUS == 24000000 + case 5: ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(4); break; + default: ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(5); +#else +#error "MK20DX128 bus frequency must be 48 or 24 MHz" +#endif + } + + // 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; + SPI0_CTAR0 = ctar0; + SPI0_CTAR1 = ctar1; + } + SPI0_MCR = SPI_MCR_MSTR; + 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 */ +static uint8_t spiRec() { + 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 */ +static uint8_t spiRec(uint8_t* buf, size_t len) { + // clear any data in RX FIFO + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF; +#if SPI_USE_8BIT_FRAME + // initial number of bytes to push into TX FIFO + int nf = len < SPI_INITIAL_FIFO_DEPTH ? len : 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 + len - 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 len is odd + if (len & 1) { + *buf++ = spiRec(); + len--; + } + // initial number of words to push into TX FIFO + int nf = len/2 < SPI_INITIAL_FIFO_DEPTH ? len/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 + len - 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 */ +static void spiSend(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 */ +static void spiSend(const uint8_t* output, size_t len) { + // clear any data in RX FIFO + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF; +#if SPI_USE_8BIT_FRAME + // initial number of bytes to push into TX FIFO + int nf = len < SPI_INITIAL_FIFO_DEPTH ? len : SPI_INITIAL_FIFO_DEPTH; + // limit for pushing data into TX fifo + const uint8_t* limit = output + len; + for (int i = 0; i < nf; i++) { + SPI0_PUSHR = *output++; + } + // write data to TX FIFO + while (output < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_PUSHR = *output++; + 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 len is odd + if (len & 1) { + spiSend(*output++); + len--; + } + // initial number of words to push into TX FIFO + int nf = len/2 < SPI_INITIAL_FIFO_DEPTH ? len/2 : SPI_INITIAL_FIFO_DEPTH; + // limit for pushing data into TX fifo + const uint8_t* limit = output + len; + for (int i = 0; i < nf; i++) { + uint16_t w = (*output++) << 8; + w |= *output++; + SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w; + } + // write data to TX FIFO + while (output < limit) { + uint16_t w = *output++ << 8; + w |= *output++; + 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 +} +//============================================================================== +#elif defined(SOFTWARE_SPI) +#include +static +SoftSPI softSpiBus; +//------------------------------------------------------------------------------ +/** + * initialize SPI pins + */ +static void spiBegin() { + softSpiBus.begin(); +} +//------------------------------------------------------------------------------ +/** + * Initialize hardware SPI - dummy for soft SPI + */ +static void spiInit(uint8_t spiRate) {} +//------------------------------------------------------------------------------ +/** Soft SPI receive byte */ +static uint8_t spiRec() { + return softSpiBus.receive(); +} +//------------------------------------------------------------------------------ +/** Soft SPI read data */ +static uint8_t spiRec(uint8_t* buf, size_t n) { + for (size_t i = 0; i < n; i++) { + buf[i] = spiRec(); + } + return 0; +} +//------------------------------------------------------------------------------ +/** Soft SPI send byte */ +static void spiSend(uint8_t data) { + softSpiBus.send(data); +} +//------------------------------------------------------------------------------ +static void spiSend(const uint8_t* buf, size_t n) { + for (size_t i = 0; i < n; i++) { + spiSend(buf[i]); + } +} +//============================================================================== +#else +// functions for AVR hardware SPI +//------------------------------------------------------------------------------ +// make sure SPCR rate is in expected bits +#if (SPR0 != 0 || SPR1 != 1) +#error unexpected SPCR bits +#endif +//------------------------------------------------------------------------------ +/** + * initialize SPI pins + */ +static void spiBegin() { + // 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(MISO, INPUT); + pinMode(MOSI, OUTPUT); + pinMode(SCK, OUTPUT); +} +//------------------------------------------------------------------------------ +/** + * Initialize hardware SPI + * Set SCK rate to F_CPU/pow(2, 1 + spiRate) for spiRate [0,6] + */ +static void spiInit(uint8_t spiRate) { + spiRate = spiRate > 12 ? 6 : spiRate/2; + // See avr processor documentation + SPCR = (1 << SPE) | (1 << MSTR) | (spiRate >> 1); + SPSR = spiRate & 1 || spiRate == 6 ? 0 : 1 << SPI2X; +} +//------------------------------------------------------------------------------ +/** SPI receive a byte */ +static uint8_t spiRec() { + SPDR = 0XFF; + while (!(SPSR & (1 << SPIF))); + return SPDR; +} +//------------------------------------------------------------------------------ +/** SPI receive multiple bytes */ +static uint8_t spiRec(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; +} +//------------------------------------------------------------------------------ +/** SPI send a byte */ +static void spiSend(uint8_t b) { + SPDR = b; + while (!(SPSR & (1 << SPIF))); +} +//------------------------------------------------------------------------------ +static void spiSend(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 // SOFTWARE_SPI +//============================================================================== +#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 +//============================================================================== +// Sd2Card member functions +//------------------------------------------------------------------------------ +// send command and return error code. Return zero for OK +uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { + // select card + chipSelectLow(); + + // wait up to 300 ms if busy + waitNotBusy(300); + + 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) spiRec(); + + // wait for response + for (uint8_t i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++); + return status_; +} +//------------------------------------------------------------------------------ +/** + * Determine the size of an SD flash memory card. + * + * \return The number of 512 byte data blocks in the card + * or zero if an error occurs. + */ +uint32_t Sd2Card::cardSize() { + 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 Sd2Card::chipSelectHigh() { + digitalWrite(chipSelectPin_, HIGH); + // insure MISO goes high impedance + spiSend(0XFF); +} +//------------------------------------------------------------------------------ +void Sd2Card::chipSelectLow() { + spiInit(spiRate_); + digitalWrite(chipSelectPin_, LOW); +} +//------------------------------------------------------------------------------ +/** Erase a range of blocks. + * + * \param[in] firstBlock The address of the first block in the range. + * \param[in] lastBlock The address of the last block in the range. + * + * \note This function requests the SD card to do a flash erase for a + * range of blocks. The data on the card after an erase operation is + * either 0 or 1, depends on the card vendor. The card must support + * single block erase. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::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 (type_ != SD_CARD_TYPE_SDHC) { + firstBlock <<= 9; + lastBlock <<= 9; + } + if (cardCommand(CMD32, firstBlock) + || cardCommand(CMD33, lastBlock) + || cardCommand(CMD38, 0)) { + error(SD_CARD_ERROR_ERASE); + goto fail; + } + if (!waitNotBusy(SD_ERASE_TIMEOUT)) { + error(SD_CARD_ERROR_ERASE_TIMEOUT); + goto fail; + } + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Determine if card supports single block erase. + * + * \return The value one, true, is returned if single block erase is supported. + * The value zero, false, is returned if single block erase is not supported. + */ +bool Sd2Card::eraseSingleBlockEnable() { + csd_t csd; + return readCSD(&csd) ? csd.v1.erase_blk_en : false; +} +//------------------------------------------------------------------------------ +/** + * Initialize an SD flash memory card. + * + * \param[in] sckRateID SPI clock rate selector. See setSckRate(). + * \param[in] chipSelectPin SD chip select pin number. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. The reason for failure + * can be determined by calling errorCode() and errorData(). + */ +bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { + errorCode_ = type_ = 0; + chipSelectPin_ = chipSelectPin; + // 16-bit init start time allows over a minute + uint16_t t0 = (uint16_t)millis(); + uint32_t arg; + + pinMode(chipSelectPin_, OUTPUT); + digitalWrite(chipSelectPin_, HIGH); + spiBegin(); + + // set SCK rate for initialization commands + spiRate_ = SPI_SD_INIT_RATE; + spiInit(spiRate_); + + // 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++) status_ = spiRec(); + if (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 ((spiRec() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC); + // discard rest of ocr - contains allowed voltage range + for (uint8_t i = 0; i < 3; i++) spiRec(); + } + chipSelectHigh(); +#ifndef SOFTWARE_SPI + return setSckRate(sckRateID); +#else // SOFTWARE_SPI + return true; +#endif // SOFTWARE_SPI + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** + * Read a 512 byte block from an SD card. + * + * \param[in] blockNumber Logical block to be read. + * \param[out] dst Pointer to the location that will receive the data. + + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::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; +} +//------------------------------------------------------------------------------ +/** Read one data block in a multiple block read sequence + * + * \param[in] dst Pointer to the location for the data to be read. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::readData(uint8_t *dst) { + chipSelectLow(); + return readData(dst, 512); +} +//------------------------------------------------------------------------------ +bool Sd2Card::readData(uint8_t* dst, size_t count) { + uint16_t crc; + // wait for start block token + uint16_t t0 = millis(); + while ((status_ = spiRec()) == 0XFF) { + if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) { + error(SD_CARD_ERROR_READ_TIMEOUT); + goto fail; + } + } + if (status_ != DATA_START_BLOCK) { + error(SD_CARD_ERROR_READ); + goto fail; + } + // transfer data + if (status_ = spiRec(dst, count)) { + error(SD_CARD_ERROR_SPI_DMA); + goto fail; + } + // get crc + crc = (spiRec() << 8) | spiRec(); +#if USE_SD_CRC + if (crc != CRC_CCITT(dst, count)) { + error(SD_CARD_ERROR_READ_CRC); + goto fail; + } +#endif // USE_SD_CRC + + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** read CID or CSR register */ +bool Sd2Card::readRegister(uint8_t cmd, void* buf) { + uint8_t* dst = reinterpret_cast(buf); + if (cardCommand(cmd, 0)) { + error(SD_CARD_ERROR_READ_REG); + goto fail; + } + return readData(dst, 16); + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::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; +} +//------------------------------------------------------------------------------ +/** End a read multiple blocks sequence. + * +* \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::readStop() { + chipSelectLow(); + if (cardCommand(CMD12, 0)) { + error(SD_CARD_ERROR_CMD12); + goto fail; + } + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** + * Set the SPI clock rate. + * + * \param[in] sckRateID A value in the range [0, 14]. + * + * The SPI clock divisor will be set to approximately + * + * (2 + (sckRateID & 1)) << ( sckRateID/2) + * + * The maximum SPI rate is F_CPU/2 for \a sckRateID = 0 and the rate is + * F_CPU/128 for \a scsRateID = 12. + * + * \return The value one, true, is returned for success and the value zero, + * false, is returned for an invalid value of \a sckRateID. + */ +bool Sd2Card::setSckRate(uint8_t sckRateID) { + if (sckRateID > MAX_SCK_RATE_ID) { + error(SD_CARD_ERROR_SCK_RATE); + return false; + } + spiRate_ = sckRateID; + return true; +} +//------------------------------------------------------------------------------ +// wait for card to go not busy +bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) { + uint16_t t0 = millis(); + while (spiRec() != 0XFF) { + if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** + * Writes a 512 byte block to an SD card. + * + * \param[in] blockNumber Logical block to be written. + * \param[in] src Pointer to the location of the data to be written. + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::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; + // wait for flash programming to complete + if (!waitNotBusy(SD_WRITE_TIMEOUT)) { + error(SD_CARD_ERROR_WRITE_TIMEOUT); + goto fail; + } + // response is r2 so get and check two bytes for nonzero + if (cardCommand(CMD13, 0) || spiRec()) { + error(SD_CARD_ERROR_WRITE_PROGRAMMING); + goto fail; + } + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Write one data block in a multiple block write sequence + * \param[in] src Pointer to the location of the data to be written. + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::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 Sd2Card::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); + + status_ = spiRec(); + if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) { + error(SD_CARD_ERROR_WRITE); + goto fail; + } + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Start a write multiple blocks sequence. + * + * \param[in] blockNumber Address of first block in sequence. + * \param[in] eraseCount The number of blocks to be pre-erased. + * + * \note This function is used with writeData() and writeStop() + * for optimized multiple block writes. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::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; +} +//------------------------------------------------------------------------------ +/** End a write multiple blocks sequence. + * +* \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::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/Universal_Serial_Adapter/Libraries/SdFat/Sd2Card.h b/Universal_Serial_Adapter/Libraries/SdFat/Sd2Card.h new file mode 100644 index 0000000..9db7188 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/Sd2Card.h @@ -0,0 +1,233 @@ +/* Arduino Sd2Card Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino Sd2Card Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino Sd2Card Library. If not, see + * . + */ +#ifndef Sd2Card_h +#define Sd2Card_h +/** + * \file + * \brief Sd2Card class for V2 SD/SDHC cards + */ +#include +#include +#include +//------------------------------------------------------------------------------ +// SPI speed is F_CPU/2^(1 + index), 0 <= index <= 6 +/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */ +uint8_t const SPI_FULL_SPEED = 0; +/** Set SCK rate to F_CPU/3 for Due */ +uint8_t const SPI_DIV3_SPEED = 1; +/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */ +uint8_t const SPI_HALF_SPEED = 2; +/** Set SCK rate to F_CPU/6 for Due */ +uint8_t const SPI_DIV6_SPEED = 3; +/** Set SCK rate to F_CPU/8. See Sd2Card::setSckRate(). */ +uint8_t const SPI_QUARTER_SPEED = 4; +/** Set SCK rate to F_CPU/16. See Sd2Card::setSckRate(). */ +uint8_t const SPI_EIGHTH_SPEED = 6; +/** Set SCK rate to F_CPU/32. See Sd2Card::setSckRate(). */ +uint8_t const SPI_SIXTEENTH_SPEED = 8; +/** MAX rate test - see spiInit for a given chip for details */ +const uint8_t MAX_SCK_RATE_ID = 14; +//------------------------------------------------------------------------------ +/** init timeout ms */ +uint16_t const SD_INIT_TIMEOUT = 2000; +/** erase timeout ms */ +uint16_t const SD_ERASE_TIMEOUT = 10000; +/** read timeout ms */ +uint16_t const SD_READ_TIMEOUT = 300; +/** write time out ms */ +uint16_t const SD_WRITE_TIMEOUT = 600; +//------------------------------------------------------------------------------ +// SD card errors +/** timeout error for command CMD0 (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 (write stop) */ +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; +/** + * define SOFTWARE_SPI to use bit-bang SPI + */ +//------------------------------------------------------------------------------ +#if LEONARDO_SOFT_SPI && defined(__AVR_ATmega32U4__) && !defined(CORE_TEENSY) +#define SOFTWARE_SPI +#elif MEGA_SOFT_SPI&&(defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__)) +#define SOFTWARE_SPI +#elif USE_SOFTWARE_SPI +#define SOFTWARE_SPI +#endif // LEONARDO_SOFT_SPI +//------------------------------------------------------------------------------ +// define default chip select pin +// +#ifndef SOFTWARE_SPI +// hardware pin defs +/** The default chip select pin for the SD card is SS. */ +uint8_t const SD_CHIP_SELECT_PIN = SS; +#else // SOFTWARE_SPI +/** SPI chip select pin */ +uint8_t const SD_CHIP_SELECT_PIN = SOFT_SPI_CS_PIN; +#endif // SOFTWARE_SPI +//------------------------------------------------------------------------------ +/** + * \class Sd2Card + * \brief Raw access to SD and SDHC flash memory cards. + */ +class Sd2Card { + public: + /** Construct an instance of Sd2Card. */ + Sd2Card() : errorCode_(SD_CARD_ERROR_INIT_NOT_CALLED), type_(0) {} + uint32_t cardSize(); + bool erase(uint32_t firstBlock, uint32_t lastBlock); + bool eraseSingleBlockEnable(); + /** + * Set SD error code. + * \param[in] code value for error code. + */ + void error(uint8_t code) {errorCode_ = code;} + /** + * \return error code for last error. See Sd2Card.h for a list of error codes. + */ + int errorCode() const {return errorCode_;} + /** \return error data for last error. */ + int errorData() const {return status_;} + /** + * Initialize an SD flash memory card with default clock rate and chip + * select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). + * + * \return true for success or false for failure. + */ + bool init(uint8_t sckRateID = SPI_FULL_SPEED, + uint8_t chipSelectPin = SD_CHIP_SELECT_PIN); + bool readBlock(uint32_t block, uint8_t* dst); + /** + * 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); + } + bool readData(uint8_t *dst); + bool readStart(uint32_t blockNumber); + bool readStop(); + bool setSckRate(uint8_t sckRateID); + /** Return the card type: SD V1, SD V2 or SDHC + * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. + */ + int type() const {return type_;} + bool writeBlock(uint32_t blockNumber, const uint8_t* src); + bool writeData(const uint8_t* src); + bool writeStart(uint32_t blockNumber, uint32_t eraseCount); + bool writeStop(); + + private: + //---------------------------------------------------------------------------- + uint8_t chipSelectPin_; + uint8_t errorCode_; + uint8_t spiRate_; + uint8_t status_; + uint8_t type_; + // private functions + uint8_t cardAcmd(uint8_t cmd, uint32_t arg) { + cardCommand(CMD55, 0); + return cardCommand(cmd, arg); + } + uint8_t cardCommand(uint8_t cmd, uint32_t arg); + bool readData(uint8_t* dst, size_t count); + bool readRegister(uint8_t cmd, void* buf); + void chipSelectHigh(); + void chipSelectLow(); + void type(uint8_t value) {type_ = value;} + bool waitNotBusy(uint16_t timeoutMillis); + bool writeData(uint8_t token, const uint8_t* src); +}; +#endif // Sd2Card_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdBaseFile.cpp b/Universal_Serial_Adapter/Libraries/SdFat/SdBaseFile.cpp new file mode 100644 index 0000000..8ab37e4 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdBaseFile.cpp @@ -0,0 +1,2338 @@ +/* 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 +// macro for debug +#define DBG_FAIL_MACRO // Serial.print(__FILE__);Serial.println(__LINE__) +//------------------------------------------------------------------------------ +// pointer to cwd directory +SdBaseFile* SdBaseFile::cwd_ = 0; +// callback function for date/time +void (*SdBaseFile::dateTime_)(uint16_t* date, uint16_t* time) = 0; +//------------------------------------------------------------------------------ +// add a cluster to a file +bool SdBaseFile::addCluster() { + if (!vol_->allocContiguous(1, &curCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + // if first cluster of file link to directory entry + if (firstCluster_ == 0) { + firstCluster_ = curCluster_; + flags_ |= F_FILE_DIR_DIRTY; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// Add a cluster to a directory file and zero the cluster. +// return with first block of cluster in the cache +cache_t* SdBaseFile::addDirCluster() { + uint32_t block; + cache_t* pc; + // max folder size + if (fileSize_/sizeof(dir_t) >= 0XFFFF) { + DBG_FAIL_MACRO; + goto fail; + } + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + block = vol_->clusterStartBlock(curCluster_); + pc = vol_->cacheFetch(block, SdVolume::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 < vol_->blocksPerCluster_; i++) { + if (!vol_->writeBlock(block + i, pc->data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // Increase directory file size by cluster size + fileSize_ += 512UL*vol_->blocksPerCluster_; + return pc; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +// cache a file's directory entry +// return pointer to cached entry or null for failure +dir_t* SdBaseFile::cacheDirEntry(uint8_t action) { + cache_t* pc; + pc = vol_->cacheFetch(dirBlock_, action); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + return pc->dir + dirIndex_; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +/** Close a file and force cached data and directory information + * to be written to the storage device. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include no file is open or an I/O error. + */ +bool SdBaseFile::close() { + bool rtn = sync(); + type_ = FAT_FILE_TYPE_CLOSED; + return rtn; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include file is not contiguous, file has zero length + * or an I/O error occurred. + */ +bool SdBaseFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { + // error if no blocks + if (firstCluster_ == 0) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint32_t c = firstCluster_; ; c++) { + uint32_t next; + if (!vol_->fatGet(c, &next)) { + DBG_FAIL_MACRO; + goto fail; + } + // check for contiguous + if (next != (c + 1)) { + // error if not end of chain + if (!vol_->isEOC(next)) { + DBG_FAIL_MACRO; + goto fail; + } + *bgnBlock = vol_->clusterStartBlock(firstCluster_); + *endBlock = vol_->clusterStartBlock(c) + + vol_->blocksPerCluster_ - 1; + return true; + } + } + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include \a path contains + * an invalid DOS 8.3 file name, the FAT volume has not been initialized, + * a file is already open, the file already exists, the root + * directory is full or an I/O error. + * + */ +bool SdBaseFile::createContiguous(SdBaseFile* 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) >> (vol_->clusterSizeShift_ + 9)) + 1; + + // allocate clusters + if (!vol_->allocContiguous(count, &firstCluster_)) { + remove(); + DBG_FAIL_MACRO; + goto fail; + } + fileSize_ = size; + + // insure sync() will update dir entry + flags_ |= F_FILE_DIR_DIRTY; + + return sync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Return a file's directory entry. + * + * \param[out] dir Location for return of the file's directory entry. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::dirEntry(dir_t* dir) { + dir_t* p; + // make sure fields on SD are correct + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // read entry + p = cacheDirEntry(SdVolume::CACHE_FOR_READ); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // copy to caller's struct + memcpy(dir, p, sizeof(dir_t)); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** 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. + */ +void SdBaseFile::dirName(const dir_t& dir, char* name) { + uint8_t j = 0; + for (uint8_t i = 0; i < 11; i++) { + if (dir.name[i] == ' ')continue; + if (i == 8) name[j++] = '.'; + name[j++] = dir.name[i]; + } + name[j] = 0; +} +//------------------------------------------------------------------------------ +/** Test for the existence of a file in a directory + * + * \param[in] name Name 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 SdBaseFile::exists(const char* name) { + SdBaseFile file; + return file.open(this, name, 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 SdBaseFile::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; +} +//------------------------------------------------------------------------------ +/** Get a file's name + * + * \param[out] name An array of 13 characters for the file's name. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::getFilename(char* name) { + dir_t* p; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (isRoot()) { + name[0] = '/'; + name[1] = '\0'; + return true; + } + // cache entry + p = cacheDirEntry(SdVolume::CACHE_FOR_READ); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // format name + dirName(*p, name); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +void SdBaseFile::getpos(FatPos_t* pos) { + pos->position = curPosition_; + pos->cluster = curCluster_; +} +//------------------------------------------------------------------------------ +/** List directory contents to stdOut. + * + * \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 SdBaseFile::ls(uint8_t flags) { + ls(SdFat::stdOut(), flags, 0); +} +//------------------------------------------------------------------------------ +/** 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 SdBaseFile::ls(Print* pr, uint8_t flags, uint8_t indent) { + rewind(); + int8_t status; + while ((status = lsPrintNext(pr, flags, indent))) { + if (status > 1 && (flags & LS_R)) { + uint16_t index = curPosition()/32 - 1; + SdBaseFile s; + if (s.open(this, index, O_READ)) s.ls(pr, flags, indent + 2); + seekSet(32 * (index + 1)); + } + } +} +//------------------------------------------------------------------------------ +// saves 32 bytes on stack for ls recursion +// return 0 - EOF, 1 - normal file, or 2 - directory +int8_t SdBaseFile::lsPrintNext(Print *pr, uint8_t flags, uint8_t indent) { + dir_t dir; + uint8_t w = 0; + + while (1) { + if (read(&dir, sizeof(dir)) != sizeof(dir)) return 0; + if (dir.name[0] == DIR_NAME_FREE) return 0; + + // skip deleted entry and entries for . and .. + if (dir.name[0] != DIR_NAME_DELETED && dir.name[0] != '.' + && DIR_IS_FILE_OR_SUBDIR(&dir)) break; + } + // indent for dir level + for (uint8_t i = 0; i < indent; i++) pr->write(' '); + + // print name + for (uint8_t i = 0; i < 11; i++) { + if (dir.name[i] == ' ')continue; + if (i == 8) { + pr->write('.'); + w++; + } + pr->write(dir.name[i]); + w++; + } + if (DIR_IS_SUBDIR(&dir)) { + pr->write('/'); + w++; + } + if (flags & (LS_DATE | LS_SIZE)) { + while (w++ < 14) pr->write(' '); + } + // print modify date/time if requested + if (flags & LS_DATE) { + pr->write(' '); + printFatDate(pr, dir.lastWriteDate); + pr->write(' '); + printFatTime(pr, dir.lastWriteTime); + } + // print size if requested + if (!DIR_IS_SUBDIR(&dir) && (flags & LS_SIZE)) { + pr->write(' '); + pr->print(dir.fileSize); + } + pr->println(); + return DIR_IS_FILE(&dir) ? 1 : 2; +} +//------------------------------------------------------------------------------ +// format directory name field from a 8.3 name string +bool SdBaseFile::make83Name(const char* str, uint8_t* name, const char** ptr) { + uint8_t c; + uint8_t n = 7; // max index for part before dot + uint8_t i = 0; + // blank fill name and extension + while (i < 11) name[i++] = ' '; + i = 0; + while (*str != '\0' && *str != '/') { + c = *str++; + if (c == '.') { + if (n == 10) { + // only one dot allowed + DBG_FAIL_MACRO; + goto fail; + } + n = 10; // max index for full 8.3 name + i = 8; // place for extension + } else { + // illegal FAT characters +#ifdef __AVR__ + // store chars in flash + PGM_P p = PSTR("|<>^+=?/[];,*\"\\"); + uint8_t b; + while ((b = pgm_read_byte(p++))) if (b == c) { + DBG_FAIL_MACRO; + goto fail; + } +#else // __AVR__ + // store chars in RAM + if (strchr("|<>^+=?/[];,*\"\\", c)) { + DBG_FAIL_MACRO; + goto fail; + } +#endif // __AVR__ + + // check size and only allow ASCII printable characters + if (i > n || c < 0X21 || c > 0X7E) { + DBG_FAIL_MACRO; + goto fail; + } + // only upper case allowed in 8.3 names - convert lower to upper + name[i++] = c < 'a' || c > 'z' ? c : c + ('A' - 'a'); + } + } + *ptr = str; + // must have a file name, extension is optional + return name[0] != ' '; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Make a new directory. + * + * \param[in] parent An open SdFat 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include this file is already open, \a parent is not a + * directory, \a path is invalid or already exists in \a parent. + */ +bool SdBaseFile::mkdir(SdBaseFile* parent, const char* path, bool pFlag) { + uint8_t dname[11]; + SdBaseFile dir1, dir2; + SdBaseFile* sub = &dir1; + SdBaseFile* start = parent; + + if (!parent || isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (*path == '/') { + while (*path == '/') path++; + if (!parent->isRoot()) { + if (!dir2.openRoot(parent->vol_)) { + DBG_FAIL_MACRO; + goto fail; + } + parent = &dir2; + } + } + while (1) { + if (!make83Name(path, dname, &path)) { + DBG_FAIL_MACRO; + goto fail; + } + while (*path == '/') path++; + if (!*path) break; + if (!sub->open(parent, dname, O_READ)) { + if (!pFlag || !sub->mkdir(parent, dname)) { + DBG_FAIL_MACRO; + goto fail; + } + } + if (parent != start) parent->close(); + parent = sub; + sub = parent != &dir1 ? &dir1 : &dir2; + } + return mkdir(parent, dname); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) { + uint32_t block; + dir_t d; + dir_t* p; + cache_t* pc; + + if (!parent->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + // create a normal file + if (!open(parent, dname, O_CREAT | O_EXCL | O_RDWR)) { + DBG_FAIL_MACRO; + goto fail; + } + // convert file to directory + flags_ = O_READ; + type_ = FAT_FILE_TYPE_SUBDIR; + + // allocate and zero first cluster + if (!addDirCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + // force entry to SD + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // cache entry - should already be in cache due to sync() call + p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // change directory entry attribute + p->attributes = DIR_ATT_DIRECTORY; + + // make entry for '.' + memcpy(&d, p, sizeof(d)); + d.name[0] = '.'; + for (uint8_t i = 1; i < 11; i++) d.name[i] = ' '; + + // cache block for '.' and '..' + block = vol_->clusterStartBlock(firstCluster_); + pc = vol_->cacheFetch(block, SdVolume::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + // copy '.' to block + memcpy(&pc->dir[0], &d, sizeof(d)); + // make entry for '..' + d.name[1] = '.'; + if (parent->isRoot()) { + d.firstClusterLow = 0; + d.firstClusterHigh = 0; + } else { + d.firstClusterLow = parent->firstCluster_ & 0XFFFF; + d.firstClusterHigh = parent->firstCluster_ >> 16; + } + // copy '..' to block + memcpy(&pc->dir[1], &d, sizeof(d)); + // write first block + return vol_->cacheSync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ + /** 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 Values for \a oflag are constructed by a bitwise-inclusive + * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ + bool SdBaseFile::open(const char* path, uint8_t oflag) { + return open(cwd_, path, oflag); + } +//------------------------------------------------------------------------------ +/** Open a file or directory by name. + * + * \param[in] dirFile An open SdFat 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), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class. + * These functions do character at a time writes so 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 SdBaseFile object + * of file corruption may occur. + * + * \note Directory files must be opened read only. Write and truncation is + * not allowed for directory files. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include this file is already open, \a dirFile is not + * a directory, \a path is invalid, the file does not exist + * or can't be opened in the access mode specified by oflag. + */ +bool SdBaseFile::open(SdBaseFile* dirFile, const char* path, uint8_t oflag) { + uint8_t dname[11]; + SdBaseFile dir1, dir2; + SdBaseFile *parent = dirFile; + SdBaseFile *sub = &dir1; + + if (!dirFile) { + DBG_FAIL_MACRO; + goto fail; + } + // error if already open + if (isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (*path == '/') { + while (*path == '/') path++; + if (!dirFile->isRoot()) { + if (!dir2.openRoot(dirFile->vol_)) { + DBG_FAIL_MACRO; + goto fail; + } + parent = &dir2; + } + } + while (1) { + if (!make83Name(path, dname, &path)) { + DBG_FAIL_MACRO; + goto fail; + } + while (*path == '/') path++; + if (!*path) break; + if (!sub->open(parent, dname, O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + if (parent != dirFile) parent->close(); + parent = sub; + sub = parent != &dir1 ? &dir1 : &dir2; + } + return open(parent, dname, oflag); + + fail: + return false; +} +//------------------------------------------------------------------------------ +// open with filename in dname +bool SdBaseFile::open(SdBaseFile* dirFile, + const uint8_t dname[11], uint8_t oflag) { + cache_t* pc; + bool emptyFound = false; + bool fileFound = false; + uint8_t index; + dir_t* p; + + vol_ = dirFile->vol_; + + dirFile->rewind(); + // search for file + + while (dirFile->curPosition_ < dirFile->fileSize_) { + index = 0XF & (dirFile->curPosition_ >> 5); + p = dirFile->readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) { + // remember first empty slot + if (!emptyFound) { + dirBlock_ = vol_->cacheBlockNumber(); + dirIndex_ = index; + emptyFound = true; + } + // done if no entries follow + if (p->name[0] == DIR_NAME_FREE) break; + } else if (!memcmp(dname, p->name, 11)) { + fileFound = true; + break; + } + } + if (fileFound) { + // don't open existing file if O_EXCL + if (oflag & O_EXCL) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // don't create unless O_CREAT and O_WRITE + if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + if (emptyFound) { + index = dirIndex_; + p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + if (dirFile->type_ == FAT_FILE_TYPE_ROOT_FIXED) { + DBG_FAIL_MACRO; + goto fail; + } + // add and zero cluster for dirFile - first cluster is in cache for write + pc = dirFile->addDirCluster(); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + // use first entry in cluster + p = pc->dir; + index = 0; + } + // initialize as empty file + memset(p, 0, sizeof(dir_t)); + memcpy(p->name, dname, 11); + + // set timestamps + if (dateTime_) { + // call user date/time function + dateTime_(&p->creationDate, &p->creationTime); + } else { + // use default date/time + p->creationDate = FAT_DEFAULT_DATE; + p->creationTime = FAT_DEFAULT_TIME; + } + p->lastAccessDate = p->creationDate; + p->lastWriteDate = p->creationDate; + p->lastWriteTime = p->creationTime; + + // write entry to SD + if (!dirFile->vol_->cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + } + // open entry in cache + return openCachedEntry(index, oflag); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Open a file by index. + * + * \param[in] dirFile An open SdFat 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 Values for \a oflag are constructed by a bitwise-inclusive + * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ +bool SdBaseFile::open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag) { + dir_t* p; + + vol_ = dirFile->vol_; + + // error if already open + if (isOpen() || !dirFile) { + 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; + } + // seek to location of entry + if (!dirFile->seekSet(32 * index)) { + DBG_FAIL_MACRO; + goto fail; + } + // read entry into cache + p = dirFile->readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // error if empty slot or '.' or '..' + if (p->name[0] == DIR_NAME_FREE || + p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { + DBG_FAIL_MACRO; + goto fail; + } + // open cached entry + return openCachedEntry(index & 0XF, oflag); + + fail: + return false; +} +//------------------------------------------------------------------------------ +// open a cached directory entry. Assumes vol_ is initialized +bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { + // location of entry in cache + dir_t* p = &vol_->cacheAddress()->dir[dirIndex]; + + // write or truncate is an error for a directory or read-only file + if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) { + if (oflag & (O_WRITE | O_TRUNC)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // remember location of directory entry on SD + dirBlock_ = vol_->cacheBlockNumber(); + dirIndex_ = dirIndex; + + // copy first cluster number for directory fields + firstCluster_ = (uint32_t)p->firstClusterHigh << 16; + firstCluster_ |= p->firstClusterLow; + + // make sure it is a normal file or subdirectory + if (DIR_IS_FILE(p)) { + fileSize_ = p->fileSize; + type_ = FAT_FILE_TYPE_NORMAL; + } else if (DIR_IS_SUBDIR(p)) { + if (!setDirSize()) { + DBG_FAIL_MACRO; + goto fail; + } + type_ = FAT_FILE_TYPE_SUBDIR; + } else { + DBG_FAIL_MACRO; + goto fail; + } + // save open flags for read/write + flags_ = oflag & F_OFLAG; + + // set to start of file + curCluster_ = 0; + curPosition_ = 0; + if ((oflag & O_TRUNC) && !truncate(0)) { + DBG_FAIL_MACRO; + goto fail; + } + return oflag & O_AT_END ? seekEnd(0) : true; + + fail: + type_ = FAT_FILE_TYPE_CLOSED; + return false; +} +//------------------------------------------------------------------------------ +/** Open the next file or subdirectory in a directory. + * + * \param[in] dirFile An open SdFat instance for the directory containing the + * file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ +bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) { + dir_t* p; + uint8_t index; + + if (!dirFile) { + DBG_FAIL_MACRO; + goto fail; + } + // error if already open + if (isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + vol_ = dirFile->vol_; + + while (1) { + index = 0XF & (dirFile->curPosition_ >> 5); + + // read entry into cache + p = dirFile->readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // done if last entry + if (p->name[0] == DIR_NAME_FREE) { + DBG_FAIL_MACRO; + goto fail; + } + // skip empty slot or '.' or '..' + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { + continue; + } + // must be file or dir + if (DIR_IS_FILE_OR_SUBDIR(p)) { + return openCachedEntry(index, oflag); + } + } + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Open a directory's parent directory. + * + * \param[in] dir Parent of this directory will be opened. Must not be root. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::openParent(SdBaseFile* dir) { + dir_t entry; + dir_t* p; + SdBaseFile file; + uint32_t c; + uint32_t cluster; + uint32_t lbn; + cache_t* pc; + // error if already open or dir is root or dir is not a directory + if (isOpen() || !dir || dir->isRoot() || !dir->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + vol_ = dir->vol_; + // position to '..' + if (!dir->seekSet(32)) { + DBG_FAIL_MACRO; + goto fail; + } + // read '..' entry + if (dir->read(&entry, sizeof(entry)) != 32) { + DBG_FAIL_MACRO; + goto fail; + } + // verify it is '..' + if (entry.name[0] != '.' || entry.name[1] != '.') { + DBG_FAIL_MACRO; + goto fail; + } + // start cluster for '..' + cluster = entry.firstClusterLow; + cluster |= (uint32_t)entry.firstClusterHigh << 16; + if (cluster == 0) return openRoot(vol_); + // start block for '..' + lbn = vol_->clusterStartBlock(cluster); + // first block of parent dir + pc = vol_->cacheFetch(lbn, SdVolume::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + p = &pc->dir[1]; + // verify name for '../..' + if (p->name[0] != '.' || p->name[1] != '.') { + DBG_FAIL_MACRO; + goto fail; + } + // '..' is pointer to first cluster of parent. open '../..' to find parent + if (p->firstClusterHigh == 0 && p->firstClusterLow == 0) { + if (!file.openRoot(dir->volume())) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + if (!file.openCachedEntry(1, O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // search for parent in '../..' + do { + if (file.readDir(&entry) != 32) { + DBG_FAIL_MACRO; + goto fail; + } + c = entry.firstClusterLow; + c |= (uint32_t)entry.firstClusterHigh << 16; + } while (c != cluster); + // open parent + return open(&file, file.curPosition()/32 - 1, O_READ); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Open a volume's root directory. + * + * \param[in] vol The FAT volume containing the root directory to be opened. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include the file is already open, the FAT volume has + * not been initialized or it a FAT12 volume. + */ +bool SdBaseFile::openRoot(SdVolume* vol) { + // error if file is already open + if (isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + vol_ = vol; + if (vol->fatType() == 16 || (FAT12_SUPPORT && vol->fatType() == 12)) { + type_ = FAT_FILE_TYPE_ROOT_FIXED; + firstCluster_ = 0; + fileSize_ = 32 * vol->rootDirEntryCount(); + } else if (vol->fatType() == 32) { + type_ = FAT_FILE_TYPE_ROOT32; + firstCluster_ = vol->rootDirStart(); + if (!setDirSize()) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // volume is not initialized, invalid, or FAT12 without support + DBG_FAIL_MACRO; + goto fail; + } + // read only + flags_ = O_READ; + + // set to start of file + curCluster_ = 0; + curPosition_ = 0; + + // root has no directory entry + dirBlock_ = 0; + dirIndex_ = 0; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Return the next available byte without consuming it. + * + * \return The byte if no error and not at eof else -1; + */ +int SdBaseFile::peek() { + FatPos_t pos; + getpos(&pos); + int c = read(); + if (c >= 0) setpos(&pos); + return c; +} +//------------------------------------------------------------------------------ +/** %Print the name field of a directory entry in 8.3 format to stdOut. + * + * \param[in] dir The directory structure containing the name. + * \param[in] width Blank fill name if length is less than \a width. + * \param[in] printSlash Print '/' after directory names if true. + */ +void SdBaseFile::printDirName(const dir_t& dir, + uint8_t width, bool printSlash) { + printDirName(SdFat::stdOut(), dir, width, printSlash); +} +//------------------------------------------------------------------------------ +/** %Print the name field of a directory entry in 8.3 format. + * \param[in] pr Print stream for output. + * \param[in] dir The directory structure containing the name. + * \param[in] width Blank fill name if length is less than \a width. + * \param[in] printSlash Print '/' after directory names if true. + */ +void SdBaseFile::printDirName(Print* pr, const dir_t& dir, + uint8_t width, bool printSlash) { + uint8_t w = 0; + for (uint8_t i = 0; i < 11; i++) { + if (dir.name[i] == ' ')continue; + if (i == 8) { + pr->write('.'); + w++; + } + pr->write(dir.name[i]); + w++; + } + if (DIR_IS_SUBDIR(&dir) && printSlash) { + pr->write('/'); + w++; + } + while (w < width) { + pr->write(' '); + w++; + } +} +//------------------------------------------------------------------------------ +// print uint8_t with width 2 +static void print2u(Print* pr, uint8_t v) { + if (v < 10) pr->write('0'); + pr->print(v, DEC); +} +//------------------------------------------------------------------------------ +/** Print a file's creation date and time + * + * \param[in] pr Print stream for output. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::printCreateDateTime(Print* 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; +} +//------------------------------------------------------------------------------ +/** %Print a directory date field to stdOut. + * + * Format is yyyy-mm-dd. + * + * \param[in] fatDate The date field from a directory entry. + */ +void SdBaseFile::printFatDate(uint16_t fatDate) { + printFatDate(SdFat::stdOut(), fatDate); +} +//------------------------------------------------------------------------------ +/** %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. + */ +void SdBaseFile::printFatDate(Print* pr, uint16_t fatDate) { + pr->print(FAT_YEAR(fatDate)); + pr->write('-'); + print2u(pr, FAT_MONTH(fatDate)); + pr->write('-'); + print2u(pr, FAT_DAY(fatDate)); +} +//------------------------------------------------------------------------------ +/** %Print a directory time field to stdOut. + * + * Format is hh:mm:ss. + * + * \param[in] fatTime The time field from a directory entry. + */ +void SdBaseFile::printFatTime(uint16_t fatTime) { + printFatTime(SdFat::stdOut(), fatTime); +} +//------------------------------------------------------------------------------ +/** %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. + */ +void SdBaseFile::printFatTime(Print* 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 SdBaseFile::printField() */ +template +static int printFieldT(SdBaseFile* 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'; + } + } + do { + Type m = value; + value /= 10; + *--str = '0' + m - 10*value; + } while(value); + if (sign) { + *--str = sign; + } + return file->write(str, &buf[sizeof(buf)] - str); +} +//------------------------------------------------------------------------------ +/** 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 SdBaseFile::printField(uint16_t value, char term) { + return printFieldT(this, 0, value, 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 SdBaseFile::printField(int16_t value, char term) { + char sign = 0; + if (value < 0) { + sign = '-'; + value = -value; + } + return printFieldT(this, sign, (uint16_t)value, 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 SdBaseFile::printField(uint32_t value, char term) { + return printFieldT(this, 0, value, 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 SdBaseFile::printField(int32_t value, char term) { + char sign = 0; + if (value < 0) { + sign = '-'; + value = -value; + } + return printFieldT(this, sign, (uint32_t)value, term); +} +//------------------------------------------------------------------------------ +/** Print a file's modify date and time + * + * \param[in] pr Print stream for output. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::printModifyDateTime(Print* 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; +} +//------------------------------------------------------------------------------ +/** Print a file's name + * + * \param[in] pr Print stream for output. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::printName(Print* pr) { + char name[13]; + if (!getFilename(name)) { + DBG_FAIL_MACRO; + goto fail; + } + return pr->print(name) > 0; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Print a file's name to stdOut + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::printName() { + return printName(SdFat::stdOut()); +} +//------------------------------------------------------------------------------ +/** Read the next byte from a file. + * + * \return For success read returns the next byte in the file as an int. + * If an error occurs or end of file is reached -1 is returned. + */ +int16_t SdBaseFile::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 SdBaseFile::read(void* buf, size_t nbyte) { + uint8_t blockOfCluster; + 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 or write only + if (!isOpen() || !(flags_ & O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + // max bytes left in file + if (nbyte >= (fileSize_ - curPosition_)) { + nbyte = fileSize_ - curPosition_; + } + // amount left to read + toRead = nbyte; + while (toRead > 0) { + size_t n; + offset = curPosition_ & 0X1FF; // offset in block + blockOfCluster = vol_->blockOfCluster(curPosition_); + if (type_ == FAT_FILE_TYPE_ROOT_FIXED) { + block = vol_->rootDirStart() + (curPosition_ >> 9); + } else { + if (offset == 0 && blockOfCluster == 0) { + // start of new cluster + if (curPosition_ == 0) { + // use first cluster in file + curCluster_ = firstCluster_; + } else { + // get next cluster from FAT + if (!vol_->fatGet(curCluster_, &curCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; + } + if (offset != 0 || toRead < 512 || block == 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 = vol_->cacheFetch(block, SdVolume::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + uint8_t* src = pc->data + offset; + memcpy(dst, src, n); + } else if (!USE_MULTI_BLOCK_SD_IO || toRead < 1024) { + // read single block + n = 512; + if (!vol_->readBlock(block, dst)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + uint8_t nb = toRead >> 9; + if (type_ != FAT_FILE_TYPE_ROOT_FIXED) { + uint8_t mb = vol_->blocksPerCluster() - blockOfCluster; + if (mb < nb) nb = mb; + } + n = 512*nb; + if (vol_->cacheBlockNumber() <= block + && block < (vol_->cacheBlockNumber() + nb)) { + // flush cache if a block is in the cache + if (!vol_->cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + } + if (!vol_->sdCard()->readStart(block)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t b = 0; b < nb; b++) { + if (!vol_->sdCard()->readData(dst + b*512)) { + DBG_FAIL_MACRO; + goto fail; + } + } + if (!vol_->sdCard()->readStop()) { + DBG_FAIL_MACRO; + goto fail; + } + } + dst += n; + curPosition_ += n; + toRead -= n; + } + return nbyte; + + fail: + return -1; +} +//------------------------------------------------------------------------------ +/** 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 SdBaseFile::readDir(dir_t* dir) { + int16_t n; + // if not a directory file or miss-positioned return an error + if (!isDir() || (0X1F & 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* SdBaseFile::readDirCache() { + uint8_t i; + // error if not directory + if (!isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + // index of entry in cache + i = (curPosition_ >> 5) & 0XF; + + // use read to locate and cache block + if (read() < 0) { + DBG_FAIL_MACRO; + goto fail; + } + // advance to next entry + curPosition_ += 31; + + // return pointer to entry + return vol_->cacheAddress()->dir + i; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include the file read-only, is a directory, + * or an I/O error occurred. + */ +bool SdBaseFile::remove() { + dir_t* d; + // free any clusters - will fail if read-only or directory + if (!truncate(0)) { + DBG_FAIL_MACRO; + goto fail; + } + // cache directory entry + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // mark entry deleted + d->name[0] = DIR_NAME_DELETED; + + // set this file closed + type_ = FAT_FILE_TYPE_CLOSED; + + // write entry to SD + return vol_->cacheSync(); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include the file is a directory, is read only, + * \a dirFile is not a directory, \a path is not found + * or an I/O error occurred. + */ +bool SdBaseFile::remove(SdBaseFile* dirFile, const char* path) { + SdBaseFile file; + if (!file.open(dirFile, path, O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + return file.remove(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include \a dirFile is not open or is not a directory + * file, newPath is invalid or already exists, or an I/O error occurs. + */ +bool SdBaseFile::rename(SdBaseFile* dirFile, const char* newPath) { + dir_t entry; + uint32_t dirCluster = 0; + SdBaseFile file; + cache_t* pc; + dir_t* d; + + // must be an open file or subdirectory + if (!(isFile() || isSubDir())) { + DBG_FAIL_MACRO; + goto fail; + } + // can't move file + if (vol_ != dirFile->vol_) { + DBG_FAIL_MACRO; + goto fail; + } + // sync() and cache directory entry + sync(); + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // save directory entry + memcpy(&entry, d, sizeof(entry)); + + // mark entry deleted + d->name[0] = DIR_NAME_DELETED; + + // make directory entry for new path + if (isFile()) { + if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRITE)) { + goto restore; + } + } else { + // don't create missing path prefix components + if (!file.mkdir(dirFile, newPath, false)) { + goto restore; + } + // save cluster containing new dot dot + dirCluster = file.firstCluster_; + } + // change to new directory entry + dirBlock_ = file.dirBlock_; + dirIndex_ = file.dirIndex_; + + // mark closed to avoid possible destructor close call + file.type_ = FAT_FILE_TYPE_CLOSED; + + // cache new directory entry + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // copy all but name field to new directory entry + memcpy(&d->attributes, &entry.attributes, sizeof(entry) - sizeof(d->name)); + + // update dot dot if directory + if (dirCluster) { + // get new dot dot + uint32_t block = vol_->clusterStartBlock(dirCluster); + pc = vol_->cacheFetch(block, SdVolume::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + memcpy(&entry, &pc->dir[1], sizeof(entry)); + + // free unused cluster + if (!vol_->freeChain(dirCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + // store new dot dot + block = vol_->clusterStartBlock(firstCluster_); + pc = vol_->cacheFetch(block, SdVolume::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + memcpy(&pc->dir[1], &entry, sizeof(entry)); + } + return vol_->cacheSync(); + + restore: + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // restore entry + d->name[0] = entry.name[0]; + vol_->cacheSync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include the file is not a directory, is the root + * directory, is not empty, or an I/O error occurred. + */ +bool SdBaseFile::rmdir() { + // must be open subdirectory + if (!isSubDir()) { + DBG_FAIL_MACRO; + goto fail; + } + rewind(); + + // make sure directory is empty + while (curPosition_ < fileSize_) { + dir_t* p = readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // done if past last used entry + if (p->name[0] == DIR_NAME_FREE) break; + // skip empty slot, '.' or '..' + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; + // error not empty + if (DIR_IS_FILE_OR_SUBDIR(p)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // convert empty directory to normal file for remove + type_ = FAT_FILE_TYPE_NORMAL; + flags_ |= O_WRITE; + return remove(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::rmRfStar() { + uint16_t index; + SdBaseFile f; + rewind(); + while (curPosition_ < fileSize_) { + // remember position + index = curPosition_/32; + + dir_t* p = readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // done if past last entry + if (p->name[0] == DIR_NAME_FREE) break; + + // skip empty slot or '.' or '..' + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; + + // skip if part of long file name or volume label in root + if (!DIR_IS_FILE_OR_SUBDIR(p)) 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.flags_ |= O_WRITE; + if (!f.remove()) { + DBG_FAIL_MACRO; + goto fail; + } + } + // position to next entry if required + if (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; +} +//------------------------------------------------------------------------------ +/** 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 SdBaseFile::open(SdBaseFile*, const char*, uint8_t). + */ +SdBaseFile::SdBaseFile(const char* path, uint8_t oflag) { + type_ = FAT_FILE_TYPE_CLOSED; + writeError = false; + open(path, oflag); +} +//------------------------------------------------------------------------------ +/** Sets a file's position. + * + * \param[in] pos The new position in bytes from the beginning of the file. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::seekSet(uint32_t pos) { + uint32_t nCur; + uint32_t nNew; + // error if file not open or seek past end of file + if (!isOpen() || pos > fileSize_) { + DBG_FAIL_MACRO; + goto fail; + } + if (type_ == FAT_FILE_TYPE_ROOT_FIXED) { + curPosition_ = pos; + goto done; + } + if (pos == 0) { + // set position to start of file + curCluster_ = 0; + curPosition_ = 0; + goto done; + } + // calculate cluster index for cur and new position + nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9); + nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9); + + if (nNew < nCur || curPosition_ == 0) { + // must follow chain from first cluster + curCluster_ = firstCluster_; + } else { + // advance from curPosition + nNew -= nCur; + } + while (nNew--) { + if (!vol_->fatGet(curCluster_, &curCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + } + curPosition_ = pos; + + done: + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// set fileSize_ for a directory +bool SdBaseFile::setDirSize() { + uint16_t s = 0; + uint32_t cluster = firstCluster_; + do { + if (!vol_->fatGet(cluster, &cluster)) { + DBG_FAIL_MACRO; + goto fail; + } + s += vol_->blocksPerCluster(); + // max size if a directory file is 4096 blocks + if (s >= 4096) { + DBG_FAIL_MACRO; + goto fail; + } + } while (!vol_->isEOC(cluster)); + fileSize_ = 512L*s; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +void SdBaseFile::setpos(FatPos_t* pos) { + curPosition_ = pos->position; + curCluster_ = pos->cluster; +} +//------------------------------------------------------------------------------ +/** The sync() call causes all modified data and directory fields + * to be written to the storage device. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include a call to sync() before a file has been + * opened or an I/O error. + */ +bool SdBaseFile::sync() { + // only allow open files and directories + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (flags_ & F_FILE_DIR_DIRTY) { + dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + // check for deleted by another open file object + if (!d || d->name[0] == DIR_NAME_DELETED) { + DBG_FAIL_MACRO; + goto fail; + } + // do not set filesize for dir files + if (!isDir()) d->fileSize = fileSize_; + + // update first cluster fields + d->firstClusterLow = firstCluster_ & 0XFFFF; + d->firstClusterHigh = firstCluster_ >> 16; + + // set modify time if user supplied a callback date/time function + if (dateTime_) { + dateTime_(&d->lastWriteDate, &d->lastWriteTime); + d->lastAccessDate = d->lastWriteDate; + } + // clear directory dirty + flags_ &= ~F_FILE_DIR_DIRTY; + } + return vol_->cacheSync(); + + fail: + writeError = true; + return false; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::timestamp(SdBaseFile* file) { + dir_t* d; + dir_t dir; + + // get timestamps + if (!file->dirEntry(&dir)) { + DBG_FAIL_MACRO; + goto fail; + } + // update directory fields + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // copy timestamps + d->lastAccessDate = dir.lastAccessDate; + d->creationDate = dir.creationDate; + d->creationTime = dir.creationTime; + d->creationTimeTenths = dir.creationTimeTenths; + d->lastWriteDate = dir.lastWriteDate; + d->lastWriteTime = dir.lastWriteTime; + + // write back entry + return vol_->cacheSync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::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* d; + + if (!isOpen() + || 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; + } + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + dirDate = FAT_DATE(year, month, day); + dirTime = FAT_TIME(hour, minute, second); + if (flags & T_ACCESS) { + d->lastAccessDate = dirDate; + } + if (flags & T_CREATE) { + d->creationDate = dirDate; + d->creationTime = dirTime; + // seems to be units of 1/100 second not 1/10 as Microsoft states + d->creationTimeTenths = second & 1 ? 100 : 0; + } + if (flags & T_WRITE) { + d->lastWriteDate = dirDate; + d->lastWriteTime = dirTime; + } + return vol_->cacheSync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include file is read only, file is a directory, + * \a length is greater than the current file size or an I/O error occurs. + */ +bool SdBaseFile::truncate(uint32_t length) { + uint32_t newPos; + // error if not a normal file or read-only + if (!isFile() || !(flags_ & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // error if length is greater than current size + if (length > fileSize_) { + DBG_FAIL_MACRO; + goto fail; + } + // fileSize and length are zero - nothing to do + if (fileSize_ == 0) return true; + + // remember position for seek after truncation + newPos = curPosition_ > length ? length : curPosition_; + + // position to last cluster in truncated file + if (!seekSet(length)) { + DBG_FAIL_MACRO; + goto fail; + } + if (length == 0) { + // free all clusters + if (!vol_->freeChain(firstCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + firstCluster_ = 0; + } else { + uint32_t toFree; + if (!vol_->fatGet(curCluster_, &toFree)) { + DBG_FAIL_MACRO; + goto fail; + } + if (!vol_->isEOC(toFree)) { + // free extra clusters + if (!vol_->freeChain(toFree)) { + DBG_FAIL_MACRO; + goto fail; + } + // current cluster is end of chain + if (!vol_->fatPutEOC(curCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + fileSize_ = length; + + // need to update directory entry + flags_ |= F_FILE_DIR_DIRTY; + + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // set file to correct position + return seekSet(newPos); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** 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 SdBaseFile::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() || !(flags_ & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // seek to end of file if append flag + if ((flags_ & O_APPEND) && curPosition_ != fileSize_) { + if (!seekEnd()) { + DBG_FAIL_MACRO; + goto fail; + } + } + while (nToWrite) { + uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); + uint16_t blockOffset = curPosition_ & 0X1FF; + if (blockOfCluster == 0 && blockOffset == 0) { + // start of new cluster + if (curCluster_ != 0) { + uint32_t next; + if (!vol_->fatGet(curCluster_, &next)) { + DBG_FAIL_MACRO; + goto fail; + } + if (vol_->isEOC(next)) { + // add cluster if at end of chain + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + curCluster_ = next; + } + } else { + if (firstCluster_ == 0) { + // allocate first cluster of file + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + curCluster_ = firstCluster_; + } + } + } + // block for data write + uint32_t block = vol_->clusterStartBlock(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 && curPosition_ >= fileSize_) { + // start of new block don't need to read into cache + cacheOption = SdVolume::CACHE_RESERVE_FOR_WRITE; + } else { + // rewrite part of block + cacheOption = SdVolume::CACHE_FOR_WRITE; + } + pc = vol_->cacheFetch(block, cacheOption); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + uint8_t* dst = pc->data + blockOffset; + memcpy(dst, src, n); + if (512 == (n + blockOffset)) { + if (!vol_->cacheWriteData()) { + DBG_FAIL_MACRO; + goto fail; + } + } + } else if (!USE_MULTI_BLOCK_SD_IO || nToWrite < 1024) { + // use single block write command + n = 512; + if (vol_->cacheBlockNumber() == block) { + vol_->cacheInvalidate(); + } + if (!vol_->writeBlock(block, src)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // use multiple block write command + uint8_t maxBlocks = vol_->blocksPerCluster() - blockOfCluster; + uint8_t nBlock = nToWrite >> 9; + if (nBlock > maxBlocks) nBlock = maxBlocks; + + n = 512*nBlock; + if (!vol_->sdCard()->writeStart(block, nBlock)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t b = 0; b < nBlock; b++) { + // invalidate cache if block is in cache + if ((block + b) == vol_->cacheBlockNumber()) { + vol_->cacheInvalidate(); + } + if (!vol_->sdCard()->writeData(src + 512*b)) { + DBG_FAIL_MACRO; + goto fail; + } + } + if (!vol_->sdCard()->writeStop()) { + DBG_FAIL_MACRO; + goto fail; + } + } + curPosition_ += n; + src += n; + nToWrite -= n; + } + if (curPosition_ > fileSize_) { + // update fileSize and insure sync will update dir entry + fileSize_ = curPosition_; + flags_ |= F_FILE_DIR_DIRTY; + } else if (dateTime_ && nbyte) { + // insure sync will update modified date and time + flags_ |= F_FILE_DIR_DIRTY; + } + + if (flags_ & O_SYNC) { + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + } + return nbyte; + + fail: + // return for write error + writeError = true; + return -1; +} +//------------------------------------------------------------------------------ +// suppress cpplint warnings with NOLINT comment +#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) +void (*SdBaseFile::oldDateTime_)(uint16_t& date, uint16_t& time) = 0; // NOLINT +#endif // ALLOW_DEPRECATED_FUNCTIONS diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdBaseFile.h b/Universal_Serial_Adapter/Libraries/SdFat/SdBaseFile.h new file mode 100644 index 0000000..734876d --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdBaseFile.h @@ -0,0 +1,564 @@ +/* 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 SdBaseFile_h +#define SdBaseFile_h +/** + * \file + * \brief SdBaseFile class + */ +#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__ +#include +#include +#include +//------------------------------------------------------------------------------ +/** + * \struct FatPos_t + * \brief internal type for istream + * 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) {} +}; + +// 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; + +// SdBaseFile class static and const definitions +// flags for ls() +/** ls() flag to print modify date */ +uint8_t const LS_DATE = 1; +/** ls() flag to print file size */ +uint8_t const LS_SIZE = 2; +/** ls() flag for recursive list of subdirectories */ +uint8_t const LS_R = 4; + + +// 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; +// values for type_ +/** This file has not been opened. */ +uint8_t const FAT_FILE_TYPE_CLOSED = 0; +/** A normal file */ +uint8_t const FAT_FILE_TYPE_NORMAL = 1; +/** A FAT12 or FAT16 root directory */ +uint8_t const FAT_FILE_TYPE_ROOT_FIXED = 2; +/** A FAT32 root directory */ +uint8_t const FAT_FILE_TYPE_ROOT32 = 3; +/** A subdirectory file*/ +uint8_t const FAT_FILE_TYPE_SUBDIR = 4; +/** Test value for directory type */ +uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT_FIXED; + +/** 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); +//------------------------------------------------------------------------------ +/** + * \class SdBaseFile + * \brief Base class for SdFile with Print and C++ streams. + */ +class SdBaseFile { + public: + /** Create an instance. */ + SdBaseFile() : writeError(false), type_(FAT_FILE_TYPE_CLOSED) {} + SdBaseFile(const char* path, uint8_t oflag); +#if DESTRUCTOR_CLOSES_FILE + ~SdBaseFile() {if(isOpen()) close();} +#endif // DESTRUCTOR_CLOSES_FILE + /** + * writeError is set to true if an error occurs during a write(). + * Set writeError to false before calling print() and/or write() and check + * for true after calls to print() and/or write(). + */ + bool writeError; + /** \return value of writeError */ + bool getWriteError() {return writeError;} + /** Set writeError to zero */ + void clearWriteError() {writeError = 0;} + //---------------------------------------------------------------------------- + // helpers for stream classes + /** 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 number of bytes available from yhe current position to EOF */ + uint32_t available() {return fileSize() - curPosition();} + bool close(); + bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + bool createContiguous(SdBaseFile* dirFile, + const char* path, uint32_t size); + /** \return The current cluster number for a file or directory. */ + uint32_t curCluster() const {return curCluster_;} + /** \return The current position for a file or directory. */ + uint32_t curPosition() const {return curPosition_;} + /** \return Current working directory */ + static SdBaseFile* cwd() {return 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)) { + dateTime_ = dateTime; + } + /** Cancel the date/time callback function. */ + static void dateTimeCallbackCancel() {dateTime_ = 0;} + bool dirEntry(dir_t* dir); + static void dirName(const dir_t& dir, char* name); + bool exists(const char* name); + int16_t fgets(char* str, int16_t num, char* delim = 0); + /** \return The total number of bytes in a file or directory. */ + uint32_t fileSize() const {return fileSize_;} + /** \return The first cluster number for a file or directory. */ + uint32_t firstCluster() const {return firstCluster_;} + bool getFilename(char* name); + /** \return True if this is a directory else false. */ + bool isDir() const {return type_ >= FAT_FILE_TYPE_MIN_DIR;} + /** \return True if this is a normal file else false. */ + bool isFile() const {return type_ == FAT_FILE_TYPE_NORMAL;} + /** \return True if this is an open file/directory else false. */ + bool isOpen() const {return type_ != FAT_FILE_TYPE_CLOSED;} + /** \return True if this is a subdirectory else false. */ + bool isSubDir() const {return type_ == FAT_FILE_TYPE_SUBDIR;} + /** \return True if this is the root directory. */ + bool isRoot() const { + return type_ == FAT_FILE_TYPE_ROOT_FIXED || type_ == FAT_FILE_TYPE_ROOT32; + } + void ls(Print* pr, uint8_t flags = 0, uint8_t indent = 0); + void ls(uint8_t flags = 0); + bool mkdir(SdBaseFile* dir, const char* path, bool pFlag = true); + // alias for backward compactability + bool makeDir(SdBaseFile* dir, const char* path) { + return mkdir(dir, path, false); + } + bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag); + bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); + bool open(const char* path, uint8_t oflag = O_READ); + bool openNext(SdBaseFile* dirFile, uint8_t oflag); + bool openRoot(SdVolume* vol); + int peek(); + bool printCreateDateTime(Print* pr); + static void printFatDate(uint16_t fatDate); + static void printFatDate(Print* pr, uint16_t fatDate); + static void printFatTime(uint16_t fatTime); + static void printFatTime(Print* pr, uint16_t fatTime); + int printField(int16_t value, char term); + int printField(uint16_t value, char term); + int printField(int32_t value, char term); + int printField(uint32_t value, char term); + bool printModifyDateTime(Print* pr); + bool printName(); + bool printName(Print* pr); + int16_t read(); + int read(void* buf, size_t nbyte); + int8_t readDir(dir_t* dir); + static bool remove(SdBaseFile* dirFile, const char* path); + bool remove(); + /** Set the file's current position to zero. */ + void rewind() {seekSet(0);} + bool rename(SdBaseFile* dirFile, const char* newPath); + bool rmdir(); + // for backward compatibility + bool rmDir() {return rmdir();} + 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(curPosition_ + offset); + } + /** Set the files position to end-of-file + \a offset. See seekSet(). + * \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 seekSet(fileSize_ + offset);} + bool seekSet(uint32_t pos); + bool sync(); + bool timestamp(SdBaseFile* file); + bool timestamp(uint8_t flag, 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 type() + * if possible. + * + * \return The file or directory type. + */ + uint8_t type() const {return type_;} + bool truncate(uint32_t size); + /** \return SdVolume that contains this file. */ + SdVolume* volume() const {return vol_;} + int write(const void* buf, size_t nbyte); +//------------------------------------------------------------------------------ + private: + // allow SdFat to set cwd_ + friend class SdFat; + // global pointer to cwd dir + static SdBaseFile* cwd_; + // data time callback function + static void (*dateTime_)(uint16_t* date, uint16_t* time); + // bits defined in 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; + + // private data + uint8_t flags_; // See above for definition of flags_ bits + uint8_t fstate_; // error and eof indicator + uint8_t type_; // type of file see above for values + uint8_t dirIndex_; // index of directory entry in dirBlock + SdVolume* vol_; // volume where file is located + uint32_t curCluster_; // cluster for current file position + uint32_t curPosition_; // current file position in bytes from beginning + uint32_t dirBlock_; // block for this files directory entry + uint32_t fileSize_; // file size in bytes + uint32_t firstCluster_; // first cluster of file + + /** experimental don't use */ + bool openParent(SdBaseFile* dir); + // private functions + bool addCluster(); + cache_t* addDirCluster(); + dir_t* cacheDirEntry(uint8_t action); + int8_t lsPrintNext(Print *pr, uint8_t flags, uint8_t indent); + static bool make83Name(const char* str, uint8_t* name, const char** ptr); + bool mkdir(SdBaseFile* parent, const uint8_t dname[11]); + bool open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag); + bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags); + dir_t* readDirCache(); + bool setDirSize(); +//------------------------------------------------------------------------------ +// to be deleted + static void printDirName(const dir_t& dir, + uint8_t width, bool printSlash); + static void printDirName(Print* pr, const dir_t& dir, + uint8_t width, bool printSlash); +//------------------------------------------------------------------------------ +// Deprecated functions - suppress cpplint warnings with NOLINT comment +#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) + + public: + /** \deprecated Use: + * bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + * \param[out] bgnBlock the first block address for the file. + * \param[out] endBlock the last block address for the file. + * \return true for success or false for failure. + */ + bool contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT + return contiguousRange(&bgnBlock, &endBlock); + } + /** \deprecated Use: + * bool createContiguous(SdBaseFile* dirFile, + * const char* path, uint32_t size) + * \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 true for success or false for failure. + */ + bool createContiguous(SdBaseFile& dirFile, // NOLINT + const char* path, uint32_t size) { + return createContiguous(&dirFile, path, size); + } + /** \deprecated Use: + * static void dateTimeCallback( + * void (*dateTime)(uint16_t* date, uint16_t* time)); + * \param[in] dateTime The user's call back function. + */ + static void dateTimeCallback( + void (*dateTime)(uint16_t& date, uint16_t& time)) { // NOLINT + oldDateTime_ = dateTime; + dateTime_ = dateTime ? oldToNew : 0; + } + /** \deprecated Use: bool dirEntry(dir_t* dir); + * \param[out] dir Location for return of the file's directory entry. + * \return true for success or false for failure. + */ + bool dirEntry(dir_t& dir) {return dirEntry(&dir);} // NOLINT + /** \deprecated Use: + * bool mkdir(SdBaseFile* dir, const char* path); + * \param[in] dir An open SdFat 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. + * \return true for success or false for failure. + */ + bool mkdir(SdBaseFile& dir, const char* path) { // NOLINT + return mkdir(&dir, path); + } + /** \deprecated Use: + * bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); + * \param[in] dirFile An open SdFat instance for the directory containing the + * file to be opened. + * \param[in] path A path with a valid 8.3 DOS name for the file. + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + * \return true for success or false for failure. + */ + bool open(SdBaseFile& dirFile, // NOLINT + const char* path, uint8_t oflag) { + return open(&dirFile, path, oflag); + } + /** \deprecated Do not use in new apps + * \param[in] dirFile An open SdFat 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. + * \return true for success or false for failure. + */ + bool open(SdBaseFile& dirFile, const char* path) { // NOLINT + return open(dirFile, path, O_RDWR); + } + /** \deprecated Use: + * bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag); + * \param[in] dirFile An open SdFat 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 Values for \a oflag are constructed by a bitwise-inclusive + * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + * \return true for success or false for failure. + */ + bool open(SdBaseFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT + return open(&dirFile, index, oflag); + } + /** \deprecated Use: bool openRoot(SdVolume* vol); + * \param[in] vol The FAT volume containing the root directory to be opened. + * \return true for success or false for failure. + */ + bool openRoot(SdVolume& vol) {return openRoot(&vol);} // NOLINT + /** \deprecated Use: int8_t readDir(dir_t* dir); + * \param[out] dir The dir_t struct that will receive the data. + * \return bytes read for success zero for eof or -1 for failure. + */ + int8_t readDir(dir_t& dir) {return readDir(&dir);} // NOLINT + /** \deprecated Use: + * static uint8_t remove(SdBaseFile* dirFile, const char* path); + * \param[in] dirFile The directory that contains the file. + * \param[in] path The name of the file to be removed. + * \return true for success or false for failure. + */ + static bool remove(SdBaseFile& dirFile, const char* path) { // NOLINT + return remove(&dirFile, path); + } +//------------------------------------------------------------------------------ +// rest are private + private: + static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT + static void oldToNew(uint16_t* date, uint16_t* time) { + uint16_t d; + uint16_t t; + oldDateTime_(d, t); + *date = d; + *time = t; + } +#elif !defined(DOXYGEN) // ALLOW_DEPRECATED_FUNCTIONS + + public: + bool contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) // NOLINT + __attribute__((error("use contiguousRange(&bgnBlock, &endBlock)"))); + + bool createContiguous(SdBaseFile& dirFile, // NOLINT + const char* path, uint32_t size) + __attribute__((error("use createContiguous(&bgnBlock, &endBlock)"))); + + static void dateTimeCallback( // NOLINT + void (*dateTime)(uint16_t& date, uint16_t& time)) // NOLINT + __attribute__((error("use void dateTimeCallback(" + "void (*dateTime)(uint16_t* date, uint16_t* time))"))); + + bool dirEntry(dir_t& dir) // NOLINT + __attribute__((error("use dirEntry(&dir)"))); + + bool mkdir(SdBaseFile& dir, const char* path) // NOLINT + __attribute__((error("use mkdir(&dir, path)"))); + + bool open(SdBaseFile& dirFile, // NOLINT + const char* path, uint8_t oflag) + __attribute__((error("use open(&dirFile, path, oflag)"))); + + bool open(SdBaseFile& dirFile, const char* path) // NOLINT + __attribute__((error("use open(&dirFile, path, O_RDWR)"))); + + bool open(SdBaseFile& dirFile, uint16_t index, uint8_t oflag) // NOLINT + __attribute__((error("use open(&dirFile, index, oflag)"))); + + bool openRoot(SdVolume& vol) // NOLINT + __attribute__((error("use openRoot(&vol)"))); + + int8_t readDir(dir_t& dir) // NOLINT + __attribute__((error("use readDir(&dir)"))); + + static bool remove(SdBaseFile& dirFile, const char* path) // NOLINT + __attribute__((error("use remove(&dirFile, path)"))); +#endif // ALLOW_DEPRECATED_FUNCTIONS +}; + +#endif // SdBaseFile_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdFat.cpp b/Universal_Serial_Adapter/Libraries/SdFat/SdFat.cpp new file mode 100644 index 0000000..a3a11b9 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdFat.cpp @@ -0,0 +1,350 @@ +/* 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 +#ifndef PSTR +#define PSTR(x) x +#define PGM_P const char* +#endif +//------------------------------------------------------------------------------ +#if USE_SERIAL_FOR_STD_OUT || !defined(UDR0) +Print* SdFat::stdOut_ = &Serial; +#else // USE_SERIAL_FOR_STD_OUT +#include +Print* SdFat::stdOut_ = &MiniSerial; +#endif // USE_SERIAL_FOR_STD_OUT +//------------------------------------------------------------------------------ +static void pstrPrint(PGM_P str) { + for (uint8_t c; (c = pgm_read_byte(str)); str++) SdFat::stdOut()->write(c); +} +//------------------------------------------------------------------------------ +static void pstrPrintln(PGM_P str) { + pstrPrint(str); + SdFat::stdOut()->println(); +} +//------------------------------------------------------------------------------ +/** + * Initialize an SdFat object. + * + * Initializes the SD card, SD volume, and root directory. + * + * \param[in] chipSelectPin SD chip select pin. See Sd2Card::init(). + * \param[in] sckRateID value for SPI SCK rate. See Sd2Card::init(). + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::begin(uint8_t chipSelectPin, uint8_t sckRateID) { + return card_.init(sckRateID, chipSelectPin) && vol_.init(&card_) && chdir(1); +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::chdir(bool set_cwd) { + if (set_cwd) SdBaseFile::cwd_ = &vwd_; + if (vwd_.isOpen()) vwd_.close(); + return vwd_.openRoot(&vol_); +} +//------------------------------------------------------------------------------ +/** 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 one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::chdir(const char *path, bool set_cwd) { + SdBaseFile 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; + vwd_ = dir; + if (set_cwd) SdBaseFile::cwd_ = &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 SdFat::chvol() { + SdBaseFile::cwd_ = &vwd_; +} +//------------------------------------------------------------------------------ +/** %Print any SD error code and halt. */ +void SdFat::errorHalt() { + errorPrint(); + while (1); +} +//------------------------------------------------------------------------------ +/** %Print msg, any SD error code, and halt. + * + * \param[in] msg Message to print. + */ +void SdFat::errorHalt(char const* msg) { + errorPrint(msg); + while (1); +} +//------------------------------------------------------------------------------ +/** %Print msg, any SD error code, and halt. + * + * \param[in] msg Message in program space (flash memory) to print. + */ +void SdFat::errorHalt_P(PGM_P msg) { + errorPrint_P(msg); + while (1); +} +//------------------------------------------------------------------------------ +/** %Print any SD error code. */ +void SdFat::errorPrint() { + if (!card_.errorCode()) return; + pstrPrint(PSTR("SD errorCode: 0X")); + stdOut_->print(card_.errorCode(), HEX); + pstrPrint(PSTR(",0X")); + stdOut_->println(card_.errorData(), HEX); +} +//------------------------------------------------------------------------------ +/** %Print msg, any SD error code. + * + * \param[in] msg Message to print. + */ +void SdFat::errorPrint(char const* msg) { + pstrPrint(PSTR("error: ")); + stdOut_->println(msg); + errorPrint(); +} +//------------------------------------------------------------------------------ +/** %Print msg, any SD error code. + * + * \param[in] msg Message in program space (flash memory) to print. + */ +void SdFat::errorPrint_P(PGM_P msg) { + pstrPrint(PSTR("error: ")); + pstrPrintln(msg); + errorPrint(); +} +//------------------------------------------------------------------------------ +/** + * Test for the existence of a file. + * + * \param[in] name Name of the file to be tested for. + * + * \return true if the file exists else false. + */ +bool SdFat::exists(const char* name) { + return vwd_.exists(name); +} +//------------------------------------------------------------------------------ +/** %Print error details and halt after SdFat::init() fails. */ +void SdFat::initErrorHalt() { + initErrorPrint(); + while (1); +} +//------------------------------------------------------------------------------ +/**Print message, error details, and halt after SdFat::init() fails. + * + * \param[in] msg Message to print. + */ +void SdFat::initErrorHalt(char const *msg) { + stdOut_->println(msg); + initErrorHalt(); +} +//------------------------------------------------------------------------------ +/**Print message, error details, and halt after SdFat::init() fails. + * + * \param[in] msg Message in program space (flash memory) to print. + */ +void SdFat::initErrorHalt_P(PGM_P msg) { + pstrPrintln(msg); + initErrorHalt(); +} +//------------------------------------------------------------------------------ +/** Print error details after SdFat::init() fails. */ +void SdFat::initErrorPrint() { + if (card_.errorCode()) { + pstrPrintln(PSTR("Can't access SD card. Do not reformat.")); + if (card_.errorCode() == SD_CARD_ERROR_CMD0) { + pstrPrintln(PSTR("No card, wrong chip select pin, or SPI problem?")); + } + errorPrint(); + } else if (vol_.fatType() == 0) { + pstrPrintln(PSTR("Invalid format, reformat SD.")); + } else if (!vwd_.isOpen()) { + pstrPrintln(PSTR("Can't open root directory.")); + } else { + pstrPrintln(PSTR("No error found.")); + } +} +//------------------------------------------------------------------------------ +/**Print message and error details and halt after SdFat::init() fails. + * + * \param[in] msg Message to print. + */ +void SdFat::initErrorPrint(char const *msg) { + stdOut_->println(msg); + initErrorPrint(); +} +//------------------------------------------------------------------------------ +/**Print message and error details after SdFat::init() fails. + * + * \param[in] msg Message in program space (flash memory) to print. + */ +void SdFat::initErrorPrint_P(PGM_P msg) { + pstrPrintln(msg); + initErrorHalt(); +} +//------------------------------------------------------------------------------ +/** List the directory contents of the volume working directory to stdOut. + * + * \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 SdFat::ls(uint8_t flags) { + vwd_.ls(stdOut_, flags); +} +//------------------------------------------------------------------------------ +/** 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 SdFat::ls(Print* pr, uint8_t flags) { + vwd_.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 one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::mkdir(const char* path, bool pFlag) { + SdBaseFile 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 one, true, is returned for success and +* the value zero, false, is returned for failure. +*/ +bool SdFat::remove(const char* path) { + return SdBaseFile::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 one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::rename(const char *oldPath, const char *newPath) { + SdBaseFile file; + if (!file.open(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 one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::rmdir(const char* path) { + SdBaseFile sub; + if (!sub.open(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 one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include file is read only, file is a directory, + * \a length is greater than the current file size or an I/O error occurs. + */ +bool SdFat::truncate(const char* path, uint32_t length) { + SdBaseFile file; + if (!file.open(path, O_WRITE)) return false; + return file.truncate(length); +} diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdFat.h b/Universal_Serial_Adapter/Libraries/SdFat/SdFat.h new file mode 100644 index 0000000..c123ba4 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdFat.h @@ -0,0 +1,119 @@ +/* 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 + */ +//------------------------------------------------------------------------------ +/** SdFat version YYYYMMDD */ +#define SD_FAT_VERSION 20130207 +//------------------------------------------------------------------------------ +/** error if old IDE */ +#if !defined(ARDUINO) || ARDUINO < 100 +#error Arduino IDE must be 1.0 or greater +#endif // ARDUINO < 100 +//------------------------------------------------------------------------------ +#include +#include +#include +#include +//------------------------------------------------------------------------------ +/** + * \class SdFat + * \brief Integration class for the %SdFat library. + */ +class SdFat { + public: + SdFat() {} +#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) + /** + * Initialize an SdFat object. + * + * Initializes the SD card, SD volume, and root directory. + * + * \param[in] sckRateID value for SPI SCK rate. See Sd2Card::init(). + * \param[in] chipSelectPin SD chip select pin. See Sd2Card::init(). + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ + bool init(uint8_t sckRateID = SPI_FULL_SPEED, + uint8_t chipSelectPin = SD_CHIP_SELECT_PIN) { + return begin(chipSelectPin, sckRateID); + } +#elif !defined(DOXYGEN) // ALLOW_DEPRECATED_FUNCTIONS + bool init() __attribute__((error("use sd.begin()"))); + bool init(uint8_t sckRateID) + __attribute__((error("use sd.begin(chipSelect, sckRate)"))); + bool init(uint8_t sckRateID, uint8_t chipSelectPin) + __attribute__((error("use sd.begin(chipSelect, sckRate)"))); +#endif // ALLOW_DEPRECATED_FUNCTIONS + /** \return a pointer to the Sd2Card object. */ + Sd2Card* card() {return &card_;} + bool chdir(bool set_cwd = false); + bool chdir(const char* path, bool set_cwd = false); + void chvol(); + void errorHalt(); + void errorHalt(char const *msg); + void errorPrint(); + + void errorPrint(char const *msg); + bool exists(const char* name); + bool begin(uint8_t chipSelectPin = SD_CHIP_SELECT_PIN, + uint8_t sckRateID = SPI_FULL_SPEED); + + void initErrorHalt(); + void initErrorHalt(char const *msg); + void initErrorPrint(); + void initErrorPrint(char const *msg); + void ls(uint8_t flags = 0); + void ls(Print* pr, uint8_t flags = 0); + bool mkdir(const char* path, bool pFlag = true); + bool remove(const char* path); + bool rename(const char *oldPath, const char *newPath); + bool rmdir(const char* path); + bool truncate(const char* path, uint32_t length); + /** \return a pointer to the SdVolume object. */ + SdVolume* vol() {return &vol_;} + /** \return a pointer to the volume working directory. */ + SdBaseFile* vwd() {return &vwd_;} + //---------------------------------------------------------------------------- + void errorHalt_P(PGM_P msg); + void errorPrint_P(PGM_P msg); + void initErrorHalt_P(PGM_P msg); + void initErrorPrint_P(PGM_P msg); + //---------------------------------------------------------------------------- + /** + * Set stdOut Print stream for messages. + * \param[in] stream The new Print stream. + */ + static void setStdOut(Print* stream) {stdOut_ = stream;} + /** \return Print stream for messages. */ + static Print* stdOut() {return stdOut_;} + + private: + Sd2Card card_; + SdVolume vol_; + SdBaseFile vwd_; + static Print* stdOut_; +}; +#endif // SdFat_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdFatConfig.h b/Universal_Serial_Adapter/Libraries/SdFat/SdFatConfig.h new file mode 100644 index 0000000..9fe1e4b --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdFatConfig.h @@ -0,0 +1,183 @@ +/* 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 +//------------------------------------------------------------------------------ +/** + * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache + * for FAT table entries. 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__ +//------------------------------------------------------------------------------ +/** + * Don't use mult-block read/write on small AVR boards + */ +#if defined(RAMEND) && RAMEND < 3000 +#define USE_MULTI_BLOCK_SD_IO 0 +#else +#define USE_MULTI_BLOCK_SD_IO 1 +#endif +//------------------------------------------------------------------------------ +/** + * Force use of Arduino Standard SPI library if USE_ARDUINO_SPI_LIBRARY + * is nonzero. + */ +#define USE_ARDUINO_SPI_LIBRARY 0 +//------------------------------------------------------------------------------ +/** + * Use native SPI on Teensy 3.0 if USE_NATIVE_MK20DX128-SPI is nonzero. + */ +#if defined(__arm__) && defined(CORE_TEENSY) +#define USE_NATIVE_MK20DX128_SPI 1 +#else +#define USE_NATIVE_MK20DX128_SPI 0 +#endif +//------------------------------------------------------------------------------ +/** + * Use fast SAM3X SPI library if USE_NATIVE_SAM3X_SPI is nonzero. + */ +#if defined(__arm__) && !defined(CORE_TEENSY) +#define USE_NATIVE_SAM3X_SPI 1 +#else +#define USE_NATIVE_SAM3X_SPI 0 +#endif +//------------------------------------------------------------------------------ +/** + * 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 +//------------------------------------------------------------------------------ +/** + * To use multiple SD cards set USE_MULTIPLE_CARDS nonzero. + * + * Using multiple cards costs 400 - 500 bytes of flash. + * + * Each card requires about 550 bytes of SRAM so use of a Mega is recommended. + */ +#define USE_MULTIPLE_CARDS 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 +//------------------------------------------------------------------------------ +/** + * For AVR + * + * Set nonzero to use Serial (the HardwareSerial class) for error messages + * and output from print functions like ls(). + * + * If USE_SERIAL_FOR_STD_OUT is zero, a small non-interrupt driven class + * is used to output messages to serial port zero. This allows an alternate + * Serial library like SerialPort to be used with SdFat. + * + * You can redirect stdOut with SdFat::setStdOut(Print* stream) and + * get the current stream with SdFat::stdOut(). + */ +#define USE_SERIAL_FOR_STD_OUT 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 +//------------------------------------------------------------------------------ +/** + * Allow use of deprecated functions if ALLOW_DEPRECATED_FUNCTIONS is nonzero + */ +#define ALLOW_DEPRECATED_FUNCTIONS 0 +//------------------------------------------------------------------------------ +/** + * Allow FAT12 volumes if FAT12_SUPPORT is nonzero. + * FAT12 has not been well tested. + */ +#define FAT12_SUPPORT 0 +//------------------------------------------------------------------------------ +/** + * SPI init rate for SD initialization commands. Must be 10 (F_CPU/64) + * or greater + */ +#define SPI_SD_INIT_RATE 11 +//------------------------------------------------------------------------------ +/** + * Define MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos. + * Default pins used are SS 10, MOSI 11, MISO 12, and SCK 13. + * Edit Software Spi pins to change pin numbers. + * + * MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used + * on Mega Arduinos. Software SPI works well with GPS Shield V1.1 + * but many SD cards will fail with GPS Shield V1.0. + */ +#define MEGA_SOFT_SPI 0 +//------------------------------------------------------------------------------ +/** + * Define LEONARDO_SOFT_SPI nonzero to use software SPI on Leonardo Arduinos. + * Default pins used are SS 10, MOSI 11, MISO 12, and SCK 13. + * Edit Software Spi pins to change pin numbers. + * + * LEONARDO_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used + * on Leonardo Arduinos. Software SPI works well with GPS Shield V1.1 + * but many SD cards will fail with GPS Shield V1.0. + */ +#define LEONARDO_SOFT_SPI 0 +//------------------------------------------------------------------------------ +/** + * Set USE_SOFTWARE_SPI nonzero to always use software SPI on AVR. + */ +#define USE_SOFTWARE_SPI 0 +// define software SPI pins so Mega can use unmodified 168/328 shields +/** Default Software SPI chip select pin */ +uint8_t const SOFT_SPI_CS_PIN = 10; +/** 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; +#endif // SdFatConfig_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdFatStructs.h b/Universal_Serial_Adapter/Libraries/SdFat/SdFatStructs.h new file mode 100644 index 0000000..9bcc145 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdFatStructs.h @@ -0,0 +1,604 @@ +/* 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 SdFatStructs_h +#define SdFatStructs_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 nonexecutable 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 nonzero. 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 (nonremovable) 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 nonzero. + */ + 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 nonexecutable 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 (nonremovable) 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 nonzero, 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)); +//------------------------------------------------------------------------------ +// Definitions for directory entries +// +/** Type name for directoryEntry */ +typedef struct directoryEntry dir_t; +/** escape for name[0] = 0XE5 */ +uint8_t const DIR_NAME_0XE5 = 0X05; +/** name[0] value for entry that is free after being "deleted" */ +uint8_t const DIR_NAME_DELETED = 0XE5; +/** name[0] value for entry that is free and no allocated entries follow */ +uint8_t const DIR_NAME_FREE = 0X00; +/** file is read-only */ +uint8_t const DIR_ATT_READ_ONLY = 0X01; +/** File should hidden in directory listings */ +uint8_t const DIR_ATT_HIDDEN = 0X02; +/** Entry is for a system file */ +uint8_t const DIR_ATT_SYSTEM = 0X04; +/** Directory entry contains the volume label */ +uint8_t const DIR_ATT_VOLUME_ID = 0X08; +/** Entry is for a directory */ +uint8_t const DIR_ATT_DIRECTORY = 0X10; +/** Old DOS archive bit for backup support */ +uint8_t const DIR_ATT_ARCHIVE = 0X20; +/** Test value for long name entry. Test is + (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ +uint8_t const DIR_ATT_LONG_NAME = 0X0F; +/** Test mask for long name entry */ +uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; +/** defined attribute bits */ +uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; +/** Directory entry is part of a long name + * \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_MASK) == DIR_ATT_LONG_NAME; +} +/** Mask for file/subdirectory tests */ +uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); +/** Directory entry is for a file + * \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 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 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; +} +#endif // SdFatStructs_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdFatUtil.cpp b/Universal_Serial_Adapter/Libraries/SdFat/SdFatUtil.cpp new file mode 100644 index 0000000..9d969b2 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdFatUtil.cpp @@ -0,0 +1,77 @@ +/* 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 +#ifdef __arm__ +// should use uinstd.h to define sbrk but Due causes a conflict +extern "C" char* sbrk(int incr); +#endif // __arm__ +//------------------------------------------------------------------------------ +/** Amount of free RAM + * \return The number of free bytes. + */ +int SdFatUtil::FreeRam() { + char top; +#ifdef __arm__ + return &top - reinterpret_cast(sbrk(0)); +#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151) + extern char *__brkval; + return &top - __brkval; +#else // __arm__ + extern char *__brkval; + extern char *__malloc_heap_start; + return __brkval ? &top - __brkval : &top - __malloc_heap_start; +#endif // __arm__ +} +//------------------------------------------------------------------------------ +/** %Print a string in flash memory. + * + * \param[in] pr Print object for output. + * \param[in] str Pointer to string stored in flash memory. + */ +void SdFatUtil::print_P(Print* pr, PGM_P str) { + for (uint8_t c; (c = pgm_read_byte(str)); str++) pr->write(c); +} +//------------------------------------------------------------------------------ +/** %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 SdFatUtil::println_P(Print* pr, PGM_P str) { + print_P(pr, str); + pr->println(); +} +//------------------------------------------------------------------------------ +/** %Print a string in flash memory to Serial. + * + * \param[in] str Pointer to string stored in flash memory. + */ +void SdFatUtil::SerialPrint_P(PGM_P str) { + print_P(SdFat::stdOut(), str); +} +//------------------------------------------------------------------------------ +/** %Print a string in flash memory to Serial followed by a CR/LF. + * + * \param[in] str Pointer to string stored in flash memory. + */ +void SdFatUtil::SerialPrintln_P(PGM_P str) { + println_P(SdFat::stdOut(), str); +} diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdFatUtil.h b/Universal_Serial_Adapter/Libraries/SdFat/SdFatUtil.h new file mode 100644 index 0000000..54dbab6 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdFatUtil.h @@ -0,0 +1,40 @@ +/* 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 +/** 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 { + int FreeRam(); + void print_P(Print* pr, PGM_P str); + void println_P(Print* pr, PGM_P str); + void SerialPrint_P(PGM_P str); + void SerialPrintln_P(PGM_P str); +} +using namespace SdFatUtil; // NOLINT +#endif // #define SdFatUtil_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdFatmainpage.h b/Universal_Serial_Adapter/Libraries/SdFat/SdFatmainpage.h new file mode 100644 index 0000000..afb5847 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdFatmainpage.h @@ -0,0 +1,227 @@ +/* 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 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 only supports short 8.3 names. + +The main classes in %SdFat are SdFat, SdFile, \ref fstream, \ref ifstream, +and \ref ofstream. + +The SdFat class maintains a volume working directories, a current working +directory, and simplifies initialization of other classes. + +The SdFile class provides binary file access functions such as open(), read(), +remove(), write(), close() and sync(). This class supports access to the root +directory and subdirectories. + +The \ref fstream class implements C++ iostreams for both reading and writing +text files. + +The \ref ifstream class implements the C++ iostreams for reading text files. + +The \ref ofstream class implements the C++ iostreams for writing text files. + +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. + +The SdVolume class supports FAT16 and FAT32 partitions. Most applications +will not need to call SdVolume member function. + +The Sd2Card class supports access to standard SD cards and SDHC cards. Most +applications will not need to call Sd2Card functions. The Sd2Card class can +be used for raw access to the SD card. + +A number of example are provided in the %SdFat/examples folder. These were +developed to test %SdFat and illustrate its use. + +%SdFat was developed for high speed data recording. %SdFat was used to +implement an audio record/play class, WaveRP, for the Adafruit Wave Shield. +This application uses special Sd2Card calls to write to contiguous files in +raw mode. These functions reduce write latency so that audio can be +recorded with the small amount of RAM in the Arduino. + +\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 + Wave 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. + +\section comment Bugs and Comments + +If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net. + +\section SdFatClass SdFat Usage + +%SdFat uses a slightly restricted form of short names. +Only printable ASCII characters are supported. No characters with code point +values greater than 127 are allowed. Space is not allowed even though space +was allowed in the API of early versions of DOS. + +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. + +\note + The Arduino Print class uses character +at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink +function to control when data is written to the SD card. + +\par +An application which writes to a file using print(), println() or +\link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink +at the appropriate time to force data and directory information to be written +to the SD Card. Data and directory information are also written to the SD card +when \link SdFile::close() close() \endlink is called. + +\par +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 SdFile. A file may +be corrupted if data is written to the file by more than one instance of SdFile. + +\section HowTo How to format SD Cards as FAT Volumes + +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, a flaw in the FAT design. Also files can become +fragmented which causes reads and writes to be slower. + +A formatter sketch, SdFormatter.pde, is included in the +%SdFat/examples/SdFormatter directory. This sketch attempts to +emulate SD Association's SDFormatter. + +The best way to restore an SD card's format on a PC is to use SDFormatter +which can be downloaded from: + +http://www.sdcard.org/consumers/formatter/ + +SDFormatter aligns flash erase boundaries with file +system structures which reduces write latency and file system overhead. + +SDFormatter does not have an option for FAT type so it may format +small cards as FAT12. + +After the MBR is restored by SDFormatter you may need to reformat small +cards that have been formatted FAT12 to force the volume type to be FAT16. + +If you reformat the SD card with an OS utility, choose a cluster size that +will result in: + +4084 < CountOfClusters && CountOfClusters < 65525 + +The volume will then be FAT16. + +If you are formatting an SD card on OS X or Linux, be sure to use the first +partition. Format this partition with a cluster count in above range for FAT16. +SDHC cards should be formatted FAT32 with a cluster size of 32 KB. + +Microsoft operating systems support removable media formatted with a +Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector +in block zero. + +Microsoft operating systems expect MBR formatted removable media +to have only one partition. The first partition should be used. + +Microsoft operating systems do not support partitioning SD flash cards. +If you erase an SD card with a program like KillDisk, Most versions of +Windows will format the card as a super floppy. + +\section References References + +Adafruit Industries: + +http://www.adafruit.com/ + +http://www.ladyada.net/make/waveshield/ + +The Arduino site: + +http://www.arduino.cc/ + +For more information about FAT file systems see: + +http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + +For information about using SD cards as SPI devices see: + +http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + +The ATmega328 datasheet: + +http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf + + + */ diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdFile.cpp b/Universal_Serial_Adapter/Libraries/SdFat/SdFile.cpp new file mode 100644 index 0000000..3ad7b65 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdFile.cpp @@ -0,0 +1,83 @@ +/* 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 +/** 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 SdBaseFile::open(SdBaseFile*, const char*, uint8_t). + */ +SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { +} +//------------------------------------------------------------------------------ +/** 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 SdFile::write(const void* buf, size_t nbyte) { + return SdBaseFile::write(buf, nbyte); +} +//------------------------------------------------------------------------------ +/** 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 SdFile::write(uint8_t b) { + return SdBaseFile::write(&b, 1) == 1 ? 1 : 0; +} +//------------------------------------------------------------------------------ +/** 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 SdFile::write(const char* str) { + return SdBaseFile::write(str, strlen(str)); +} +//------------------------------------------------------------------------------ +/** Write a PROGMEM string to a file. + * \param[in] str Pointer to the PROGMEM string. + * Use getWriteError to check for errors. + */ +void SdFile::write_P(PGM_P str) { + for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c); +} +//------------------------------------------------------------------------------ +/** Write a PROGMEM string followed by CR/LF to a file. + * \param[in] str Pointer to the PROGMEM string. + * Use getWriteError to check for errors. + */ +void SdFile::writeln_P(PGM_P str) { + write_P(str); + write_P(PSTR("\r\n")); +} diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdFile.h b/Universal_Serial_Adapter/Libraries/SdFat/SdFile.h new file mode 100644 index 0000000..4c7f36a --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdFile.h @@ -0,0 +1,49 @@ +/* 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 SdFile class + */ +#include +#ifndef SdFile_h +#define SdFile_h +//------------------------------------------------------------------------------ +/** + * \class SdFile + * \brief SdBaseFile with Print. + */ +class SdFile : public SdBaseFile, public Print { + public: + SdFile() {} + SdFile(const char* name, uint8_t oflag); +#if DESTRUCTOR_CLOSES_FILE + ~SdFile() {} +#endif // DESTRUCTOR_CLOSES_FILE + /** \return value of writeError */ + bool getWriteError() {return SdBaseFile::getWriteError();} + /** Set writeError to zero */ + void clearWriteError() {SdBaseFile::clearWriteError();} + size_t write(uint8_t b); + int write(const char* str); + int write(const void* buf, size_t nbyte); + void write_P(PGM_P str); + void writeln_P(PGM_P str); +}; +#endif // SdFile_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdInfo.h b/Universal_Serial_Adapter/Libraries/SdFat/SdInfo.h new file mode 100644 index 0000000..44d9126 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdInfo.h @@ -0,0 +1,277 @@ +/* Arduino Sd2Card Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino Sd2Card Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino Sd2Card Library. If not, see + * . + */ +#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 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; +//------------------------------------------------------------------------------ +/** 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; +//------------------------------------------------------------------------------ +/** CSD 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; +//------------------------------------------------------------------------------ +/** CSD 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; +//------------------------------------------------------------------------------ +/** union of old and new style CSD register */ +union csd_t { + csd1_t v1; + csd2_t v2; +}; +#endif // SdInfo_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdStream.cpp b/Universal_Serial_Adapter/Libraries/SdFat/SdStream.cpp new file mode 100644 index 0000000..b72ca44 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdStream.cpp @@ -0,0 +1,151 @@ +/* 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 + +//============================================================================== + /// @cond SHOW_PROTECTED +int16_t SdStreamBase::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 SdStreamBase::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 (!SdBaseFile::open(path, flags)) goto fail; + setmode(mode); + clear(); + return; + + fail: + SdBaseFile::close(); + setstate(failbit); + return; +} +//------------------------------------------------------------------------------ +void SdStreamBase::putch(char c) { + if (c == '\n' && !(getmode() & ios::binary)) { + write('\r'); + } + write(c); + if (writeError) setstate(badbit); +} +//------------------------------------------------------------------------------ +void SdStreamBase::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 (writeError) setstate(badbit); +} +//------------------------------------------------------------------------------ +/** Internal do not use + * \param[in] off + * \param[in] way + */ +bool SdStreamBase::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 SdStreamBase::seekpos(pos_type pos) { + return seekSet(pos); +} +//------------------------------------------------------------------------------ +int SdStreamBase::write(const void* buf, size_t n) { + return SdBaseFile::write(buf, n); +} +//------------------------------------------------------------------------------ +void SdStreamBase::write(char c) { + write(&c, 1); +} +/// @endcond diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdStream.h b/Universal_Serial_Adapter/Libraries/SdFat/SdStream.h new file mode 100644 index 0000000..3b79fc8 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdStream.h @@ -0,0 +1,263 @@ +/* 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 SdStream_h +#define SdStream_h +/** + * \file + * \brief \ref fstream, \ref ifstream, and \ref ofstream classes + */ +#include +#include +//============================================================================== +/** + * \class SdStreamBase + * \brief Base class for SD streams + */ +class SdStreamBase : protected SdBaseFile, 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 mode_;} + /** Internal do not use + * \param[in] mode + */ + void setmode(ios::openmode mode) {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 mode_; +}; +//============================================================================== +/** + * \class fstream + * \brief SD file input/output stream. + */ +class fstream : public iostream, SdStreamBase { + 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); + SdBaseFile::writeError = false; + } + /** Close a file and force cached data and directory information + * to be written to the storage device. + */ + void close() {SdBaseFile::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) { + SdStreamBase::open(path, mode); + } + /** \return True if stream is open else false. */ + bool is_open () {return SdBaseFile::isOpen();} + + protected: + /// @cond SHOW_PROTECTED + /** Internal - do not use + * \return + */ + int16_t getch() {return SdStreamBase::getch();} + /** Internal - do not use + * \param[out] pos + */ + void getpos(FatPos_t* pos) {SdBaseFile::getpos(pos);} + /** Internal - do not use + * \param[in] c + */ + void putch(char c) {SdStreamBase::putch(c);} + /** Internal - do not use + * \param[in] str + */ + void putstr(const char *str) {SdStreamBase::putstr(str);} + /** Internal - do not use + * \param[in] pos + */ + bool seekoff(off_type off, seekdir way) { + return SdStreamBase::seekoff(off, way); + } + bool seekpos(pos_type pos) {return SdStreamBase::seekpos(pos);} + void setpos(FatPos_t* pos) {SdBaseFile::setpos(pos);} + bool sync() {return SdStreamBase::sync();} + pos_type tellpos() {return SdStreamBase::curPosition();} + /// @endcond +}; +//============================================================================== +/** + * \class ifstream + * \brief SD file input stream. + */ +class ifstream : public istream, SdStreamBase { + 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() {SdBaseFile::close();} + /** \return True if stream is open else false. */ + bool is_open() {return SdBaseFile::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) { + SdStreamBase::open(path, mode | in); + } + + protected: + /// @cond SHOW_PROTECTED + /** Internal - do not use + * \return + */ + int16_t getch() {return SdStreamBase::getch();} + /** Internal - do not use + * \param[out] pos + */ + void getpos(FatPos_t* pos) {SdBaseFile::getpos(pos);} + /** Internal - do not use + * \param[in] pos + */ + bool seekoff(off_type off, seekdir way) { + return SdStreamBase::seekoff(off, way); + } + bool seekpos(pos_type pos) {return SdStreamBase::seekpos(pos);} + void setpos(FatPos_t* pos) {SdBaseFile::setpos(pos);} + pos_type tellpos() {return SdStreamBase::curPosition();} + /// @endcond +}; +//============================================================================== +/** + * \class ofstream + * \brief SD card output stream. + */ +class ofstream : public ostream, SdStreamBase { + 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); + SdBaseFile::writeError = false; + } + /** Close a file and force cached data and directory information + * to be written to the storage device. + */ + void close() {SdBaseFile::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) { + SdStreamBase::open(path, mode | out); + } + /** \return True if stream is open else false. */ + bool is_open() {return SdBaseFile::isOpen();} + + protected: + /// @cond SHOW_PROTECTED + /** + * Internal do not use + * \param[in] c + */ + void putch(char c) {SdStreamBase::putch(c);} + void putstr(const char* str) {SdStreamBase::putstr(str);} + bool seekoff(off_type off, seekdir way) { + return SdStreamBase::seekoff(off, way); + } + bool seekpos(pos_type pos) {return SdStreamBase::seekpos(pos);} + /** + * Internal do not use + * \param[in] b + */ + bool sync() {return SdStreamBase::sync();} + pos_type tellpos() {return SdStreamBase::curPosition();} + /// @endcond +}; +//------------------------------------------------------------------------------ +#endif // SdStream_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdVolume.cpp b/Universal_Serial_Adapter/Libraries/SdFat/SdVolume.cpp new file mode 100644 index 0000000..49ea882 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdVolume.cpp @@ -0,0 +1,599 @@ +/* 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 +// macro for debug +#define DBG_FAIL_MACRO // Serial.print(__FILE__);Serial.println(__LINE__) +//------------------------------------------------------------------------------ +#if !USE_MULTIPLE_CARDS +// raw block cache + +cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card +uint32_t SdVolume::cacheBlockNumber_; // current block number +uint8_t SdVolume::cacheStatus_; // status of cache block +uint32_t SdVolume::cacheFatOffset_; // offset for mirrored FAT +#if USE_SEPARATE_FAT_CACHE +cache_t SdVolume::cacheFatBuffer_; // 512 byte cache for FAT +uint32_t SdVolume::cacheFatBlockNumber_; // current Fat block number +uint8_t SdVolume::cacheFatStatus_; // status of cache Fatblock +#endif // USE_SEPARATE_FAT_CACHE +Sd2Card* SdVolume::sdCard_; // pointer to SD card object +#endif // USE_MULTIPLE_CARDS +//------------------------------------------------------------------------------ +// find a contiguous group of clusters +bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { + // start of group + uint32_t bgnCluster; + // end of group + uint32_t endCluster; + // last cluster of FAT + uint32_t fatEnd = clusterCount_ + 1; + + // flag to save place to start next search + bool setStart; + + // set search start cluster + if (*curCluster) { + // try to make file contiguous + bgnCluster = *curCluster + 1; + + // don't save new start location + setStart = false; + } else { + // start at likely place for free cluster + bgnCluster = allocSearchStart_; + + // save next search start if one cluster + setStart = count == 1; + } + // end of group + endCluster = bgnCluster; + + // search the FAT for free clusters + for (uint32_t n = 0;; n++, endCluster++) { + // can't find space checked all clusters + if (n >= clusterCount_) { + DBG_FAIL_MACRO; + goto fail; + } + // past end - start from beginning of FAT + if (endCluster > fatEnd) { + bgnCluster = endCluster = 2; + } + uint32_t f; + if (!fatGet(endCluster, &f)) { + DBG_FAIL_MACRO; + goto fail; + } + + if (f != 0) { + // cluster in use try next cluster as bgnCluster + bgnCluster = endCluster + 1; + } else if ((endCluster - bgnCluster + 1) == count) { + // done - found space + break; + } + } + // 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--; + } + if (*curCluster != 0) { + // connect chains + if (!fatPut(*curCluster, bgnCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // return first cluster number to caller + *curCluster = bgnCluster; + + // remember possible next free cluster + if (setStart) allocSearchStart_ = bgnCluster + 1; + + return true; + + fail: + return false; +} +//============================================================================== +// cache functions +#if USE_SEPARATE_FAT_CACHE +//------------------------------------------------------------------------------ +cache_t* SdVolume::cacheFetch(uint32_t blockNumber, uint8_t options) { + return cacheFetchData(blockNumber, options); +} +//------------------------------------------------------------------------------ +cache_t* SdVolume::cacheFetchData(uint32_t blockNumber, uint8_t options) { + if (cacheBlockNumber_ != blockNumber) { + if (!cacheWriteData()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!(options & CACHE_OPTION_NO_READ)) { + if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + cacheStatus_ = 0; + cacheBlockNumber_ = blockNumber; + } + cacheStatus_ |= options & CACHE_STATUS_MASK; + return &cacheBuffer_; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +cache_t* SdVolume::cacheFetchFat(uint32_t blockNumber, uint8_t options) { + if (cacheFatBlockNumber_ != blockNumber) { + if (!cacheWriteFat()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!(options & CACHE_OPTION_NO_READ)) { + if (!sdCard_->readBlock(blockNumber, cacheFatBuffer_.data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + cacheFatStatus_ = 0; + cacheFatBlockNumber_ = blockNumber; + } + cacheFatStatus_ |= options & CACHE_STATUS_MASK; + return &cacheFatBuffer_; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +bool SdVolume::cacheSync() { + return cacheWriteData() && cacheWriteFat(); +} +//------------------------------------------------------------------------------ +bool SdVolume::cacheWriteData() { + if (cacheStatus_ & CACHE_STATUS_DIRTY) { + if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) { + DBG_FAIL_MACRO; + goto fail; + } + cacheStatus_ &= ~CACHE_STATUS_DIRTY; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool SdVolume::cacheWriteFat() { + if (cacheFatStatus_ & CACHE_STATUS_DIRTY) { + if (!sdCard_->writeBlock(cacheFatBlockNumber_, cacheFatBuffer_.data)) { + DBG_FAIL_MACRO; + goto fail; + } + // mirror second FAT + if (cacheFatOffset_) { + uint32_t lbn = cacheFatBlockNumber_ + cacheFatOffset_; + if (!sdCard_->writeBlock(lbn, cacheFatBuffer_.data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + cacheFatStatus_ &= ~CACHE_STATUS_DIRTY; + } + return true; + + fail: + return false; +} +#else // USE_SEPARATE_FAT_CACHE +//------------------------------------------------------------------------------ +cache_t* SdVolume::cacheFetch(uint32_t blockNumber, uint8_t options) { + if (cacheBlockNumber_ != blockNumber) { + if (!cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!(options & CACHE_OPTION_NO_READ)) { + if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + cacheStatus_ = 0; + cacheBlockNumber_ = blockNumber; + } + cacheStatus_ |= options & CACHE_STATUS_MASK; + return &cacheBuffer_; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +cache_t* SdVolume::cacheFetchFat(uint32_t blockNumber, uint8_t options) { + return cacheFetch(blockNumber, options | CACHE_STATUS_FAT_BLOCK); +} +//------------------------------------------------------------------------------ +bool SdVolume::cacheSync() { + if (cacheStatus_ & CACHE_STATUS_DIRTY) { + if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) { + DBG_FAIL_MACRO; + goto fail; + } + // mirror second FAT + if ((cacheStatus_ & CACHE_STATUS_FAT_BLOCK) && cacheFatOffset_) { + uint32_t lbn = cacheBlockNumber_ + cacheFatOffset_; + if (!sdCard_->writeBlock(lbn, cacheBuffer_.data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + cacheStatus_ &= ~CACHE_STATUS_DIRTY; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool SdVolume::cacheWriteData() { + return cacheSync(); +} +#endif // USE_SEPARATE_FAT_CACHE +//------------------------------------------------------------------------------ +void SdVolume::cacheInvalidate() { + cacheBlockNumber_ = 0XFFFFFFFF; + cacheStatus_ = 0; +} +//============================================================================== +//------------------------------------------------------------------------------ +uint32_t SdVolume::clusterStartBlock(uint32_t cluster) const { + return dataStartBlock_ + ((cluster - 2)*blocksPerCluster_); +} +//------------------------------------------------------------------------------ +// Fetch a FAT entry +bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) { + uint32_t lba; + cache_t* pc; + // error if reserved cluster of beyond FAT + if (cluster < 2 || cluster > (clusterCount_ + 1)) { + DBG_FAIL_MACRO; + goto fail; + } + if (FAT12_SUPPORT && fatType_ == 12) { + uint16_t index = cluster; + index += index >> 1; + lba = fatStartBlock_ + (index >> 9); + pc = cacheFetchFat(lba, 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, CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + index = 0; + } + tmp |= pc->data[index] << 8; + *value = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; + return true; + } + if (fatType_ == 16) { + lba = fatStartBlock_ + (cluster >> 8); + } else if (fatType_ == 32) { + lba = fatStartBlock_ + (cluster >> 7); + } else { + DBG_FAIL_MACRO; + goto fail; + } + pc = cacheFetchFat(lba, CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + if (fatType_ == 16) { + *value = pc->fat16[cluster & 0XFF]; + } else { + *value = pc->fat32[cluster & 0X7F] & FAT32MASK; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// Store a FAT entry +bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { + uint32_t lba; + cache_t* pc; + // error if reserved cluster of beyond FAT + if (cluster < 2 || cluster > (clusterCount_ + 1)) { + DBG_FAIL_MACRO; + goto fail; + } + if (FAT12_SUPPORT && fatType_ == 12) { + uint16_t index = cluster; + index += index >> 1; + lba = fatStartBlock_ + (index >> 9); + pc = cacheFetchFat(lba, 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, 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; + } + if (fatType_ == 16) { + lba = fatStartBlock_ + (cluster >> 8); + } else if (fatType_ == 32) { + lba = fatStartBlock_ + (cluster >> 7); + } else { + DBG_FAIL_MACRO; + goto fail; + } + pc = cacheFetchFat(lba, CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + // store entry + if (fatType_ == 16) { + pc->fat16[cluster & 0XFF] = value; + } else { + pc->fat32[cluster & 0X7F] = value; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// free a cluster chain +bool SdVolume::freeChain(uint32_t cluster) { + uint32_t next; + + // clear free cluster location + allocSearchStart_ = 2; + + do { + if (!fatGet(cluster, &next)) { + DBG_FAIL_MACRO; + goto fail; + } + // free cluster + if (!fatPut(cluster, 0)) { + DBG_FAIL_MACRO; + goto fail; + } + + cluster = next; + } while (!isEOC(cluster)); + + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Volume free space in clusters. + * + * \return Count of free clusters for success or -1 if an error occurs. + */ +int32_t SdVolume::freeClusterCount() { + uint32_t free = 0; + uint32_t lba; + uint32_t todo = clusterCount_ + 2; + uint16_t n; + + if (FAT12_SUPPORT && fatType_ == 12) { + for (unsigned i = 2; i < todo; i++) { + uint32_t c; + if (!fatGet(i, &c)) { + DBG_FAIL_MACRO; + goto fail; + } + if (c == 0) free++; + } + } else if (fatType_ == 16 || fatType_ == 32) { + lba = fatStartBlock_; + while (todo) { + cache_t* pc = cacheFetchFat(lba++, CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + n = fatType_ == 16 ? 256 : 128; + if (todo < n) n = todo; + if (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; +} +//------------------------------------------------------------------------------ +/** Initialize a FAT volume. + * + * \param[in] dev The SD card where the volume is located. + * + * \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 one, true, is returned for success and + * the value zero, false, is returned for failure. Reasons for + * failure include not finding a valid partition, not finding a valid + * FAT file system in the specified partition or an I/O error. + */ +bool SdVolume::init(Sd2Card* dev, uint8_t part) { + uint32_t totalBlocks; + uint32_t volumeStartBlock = 0; + fat32_boot_t* fbs; + cache_t* pc; + sdCard_ = dev; + fatType_ = 0; + allocSearchStart_ = 2; + cacheStatus_ = 0; // cacheSync() will write block if true + cacheBlockNumber_ = 0XFFFFFFFF; + cacheFatOffset_ = 0; +#if USE_SERARATEFAT_CACHE + cacheFatStatus_ = 0; // cacheSync() will write block if true + cacheFatBlockNumber_ = 0XFFFFFFFF; +#endif // USE_SERARATEFAT_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 = cacheFetch(volumeStartBlock, CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + part_t* p = &pc->mbr.part[part-1]; + if ((p->boot & 0X7F) !=0 || + p->totalSectors < 100 || + p->firstSector == 0) { + // not a valid partition + DBG_FAIL_MACRO; + goto fail; + } + volumeStartBlock = p->firstSector; + } + pc = cacheFetch(volumeStartBlock, CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + fbs = &(pc->fbs32); + if (fbs->bytesPerSector != 512 || + fbs->fatCount == 0 || + fbs->reservedSectorCount == 0 || + fbs->sectorsPerCluster == 0) { + // not valid FAT volume + DBG_FAIL_MACRO; + goto fail; + } + fatCount_ = fbs->fatCount; + blocksPerCluster_ = fbs->sectorsPerCluster; + // determine shift that is same as multiply by blocksPerCluster_ + clusterSizeShift_ = 0; + while (blocksPerCluster_ != (1 << clusterSizeShift_)) { + // error if not power of 2 + if (clusterSizeShift_++ > 7) { + DBG_FAIL_MACRO; + goto fail; + } + } + blocksPerFat_ = fbs->sectorsPerFat16 ? + fbs->sectorsPerFat16 : fbs->sectorsPerFat32; + + if (fatCount_ > 0) cacheFatOffset_ = blocksPerFat_; + fatStartBlock_ = volumeStartBlock + fbs->reservedSectorCount; + + // count for FAT16 zero for FAT32 + rootDirEntryCount_ = fbs->rootDirEntryCount; + + // directory start for FAT16 dataStart for FAT32 + rootDirStart_ = fatStartBlock_ + fbs->fatCount * blocksPerFat_; + + // data start for FAT16 and FAT32 + dataStartBlock_ = rootDirStart_ + ((32 * fbs->rootDirEntryCount + 511)/512); + + // total blocks for FAT16 or FAT32 + totalBlocks = fbs->totalSectors16 ? + fbs->totalSectors16 : fbs->totalSectors32; + // total data blocks + clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock); + + // divide by cluster size to get cluster count + clusterCount_ >>= clusterSizeShift_; + + // FAT type is determined by cluster count + if (clusterCount_ < 4085) { + fatType_ = 12; + if (!FAT12_SUPPORT) { + DBG_FAIL_MACRO; + goto fail; + } + } else if (clusterCount_ < 65525) { + fatType_ = 16; + } else { + rootDirStart_ = fbs->fat32RootCluster; + fatType_ = 32; + } + return true; + + fail: + return false; +} diff --git a/Universal_Serial_Adapter/Libraries/SdFat/SdVolume.h b/Universal_Serial_Adapter/Libraries/SdFat/SdVolume.h new file mode 100644 index 0000000..14b1d65 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/SdVolume.h @@ -0,0 +1,234 @@ +/* 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 +#define SdVolume_h +/** + * \file + * \brief SdVolume class + */ +#include +#include +#include + +//============================================================================== +// SdVolume class +/** + * \brief Cache for an SD 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 SdVolume + * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. + */ +class SdVolume { + public: + /** Create an instance of SdVolume */ + SdVolume() : fatType_(0) {} + /** Clear the cache and returns a pointer to the cache. Used by the WaveRP + * recorder to do raw write to the SD card. Not for normal apps. + * \return A pointer to the cache buffer or zero if an error occurs. + */ + cache_t* cacheClear() { + if (!cacheSync()) return 0; + cacheBlockNumber_ = 0XFFFFFFFF; + return &cacheBuffer_; + } + /** Initialize a FAT volume. Try partition one first then try super + * floppy format. + * + * \param[in] dev The Sd2Card where the volume is located. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. Reasons for + * failure include not finding a valid partition, not finding a valid + * FAT file system or an I/O error. + */ + bool init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);} + bool init(Sd2Card* dev, uint8_t part); + + // inline functions that return volume info + /** \return The volume's cluster size in blocks. */ + uint8_t blocksPerCluster() const {return blocksPerCluster_;} + /** \return The number of blocks in one FAT. */ + uint32_t blocksPerFat() const {return blocksPerFat_;} + /** \return The total number of clusters in the volume. */ + uint32_t clusterCount() const {return clusterCount_;} + /** \return The shift count required to multiply by blocksPerCluster. */ + uint8_t clusterSizeShift() const {return clusterSizeShift_;} + /** \return The logical block number for the start of file data. */ + uint32_t dataStartBlock() const {return dataStartBlock_;} + /** \return The number of FAT structures on the volume. */ + uint8_t fatCount() const {return fatCount_;} + /** \return The logical block number for the start of the first FAT. */ + uint32_t fatStartBlock() const {return fatStartBlock_;} + /** \return The FAT type of the volume. Values are 12, 16 or 32. */ + uint8_t fatType() const {return fatType_;} + int32_t freeClusterCount(); + /** \return The number of entries in the root directory for FAT16 volumes. */ + uint32_t rootDirEntryCount() const {return rootDirEntryCount_;} + /** \return The logical block number for the start of the root directory + on FAT16 volumes or the first cluster number on FAT32 volumes. */ + uint32_t rootDirStart() const {return rootDirStart_;} + /** Sd2Card object for this volume + * \return pointer to Sd2Card object. + */ + Sd2Card* sdCard() {return sdCard_;} + /** Debug access to FAT table + * + * \param[in] n cluster number. + * \param[out] v value of entry + * \return true for success or false for failure + */ + bool dbgFat(uint32_t n, uint32_t* v) {return fatGet(n, v);} +//------------------------------------------------------------------------------ + private: + // Allow SdBaseFile access to SdVolume private data. + friend class SdBaseFile; +//------------------------------------------------------------------------------ + uint32_t allocSearchStart_; // start cluster for alloc search + uint8_t blocksPerCluster_; // cluster size in blocks + uint32_t blocksPerFat_; // FAT size in blocks + uint32_t clusterCount_; // clusters in one FAT + uint8_t clusterSizeShift_; // shift to convert cluster count to block count + uint32_t dataStartBlock_; // first data block number + uint8_t fatCount_; // number of FATs on volume + uint32_t fatStartBlock_; // start block for first FAT + uint8_t fatType_; // volume type (12, 16, OR 32) + uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir + uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32 +//------------------------------------------------------------------------------ +// block caches +// use of static functions save a bit of flash - maybe not worth complexity +// + static const uint8_t CACHE_STATUS_DIRTY = 1; + static const uint8_t CACHE_STATUS_FAT_BLOCK = 2; + static const uint8_t CACHE_STATUS_MASK + = CACHE_STATUS_DIRTY | CACHE_STATUS_FAT_BLOCK; + static const uint8_t CACHE_OPTION_NO_READ = 4; + // value for option argument in cacheFetch to indicate read from cache + static uint8_t const CACHE_FOR_READ = 0; + // value for option argument in cacheFetch to indicate write to cache + static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; + // reserve cache block with no read + static uint8_t const CACHE_RESERVE_FOR_WRITE + = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; +#if USE_MULTIPLE_CARDS + cache_t cacheBuffer_; // 512 byte cache for device blocks + uint32_t cacheBlockNumber_; // Logical number of block in the cache + uint32_t cacheFatOffset_; // offset for mirrored FAT + Sd2Card* sdCard_; // Sd2Card object for cache + uint8_t cacheStatus_; // status of cache block +#if USE_SEPARATE_FAT_CACHE + cache_t cacheFatBuffer_; // 512 byte cache for FAT + uint32_t cacheFatBlockNumber_; // current Fat block number + uint8_t cacheFatStatus_; // status of cache Fatblock +#endif // USE_SEPARATE_FAT_CACHE +#else // USE_MULTIPLE_CARDS + static cache_t cacheBuffer_; // 512 byte cache for device blocks + static uint32_t cacheBlockNumber_; // Logical number of block in the cache + static uint32_t cacheFatOffset_; // offset for mirrored FAT + static uint8_t cacheStatus_; // status of cache block +#if USE_SEPARATE_FAT_CACHE + static cache_t cacheFatBuffer_; // 512 byte cache for FAT + static uint32_t cacheFatBlockNumber_; // current Fat block number + static uint8_t cacheFatStatus_; // status of cache Fatblock +#endif // USE_SEPARATE_FAT_CACHE + static Sd2Card* sdCard_; // Sd2Card object for cache +#endif // USE_MULTIPLE_CARDS + + cache_t *cacheAddress() {return &cacheBuffer_;} + uint32_t cacheBlockNumber() {return cacheBlockNumber_;} +#if USE_MULTIPLE_CARDS + cache_t* cacheFetch(uint32_t blockNumber, uint8_t options); + cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options); + cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options); + void cacheInvalidate(); + bool cacheSync(); + bool cacheWriteData(); + bool cacheWriteFat(); +#else // USE_MULTIPLE_CARDS + static cache_t* cacheFetch(uint32_t blockNumber, uint8_t options); + static cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options); + static cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options); + static void cacheInvalidate(); + static bool cacheSync(); + static bool cacheWriteData(); + static bool cacheWriteFat(); +#endif // USE_MULTIPLE_CARDS +//------------------------------------------------------------------------------ + bool allocContiguous(uint32_t count, uint32_t* curCluster); + uint8_t blockOfCluster(uint32_t position) const { + return (position >> 9) & (blocksPerCluster_ - 1);} + uint32_t clusterStartBlock(uint32_t cluster) const; + bool 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 { + if (FAT12_SUPPORT && fatType_ == 12) return cluster >= FAT12EOC_MIN; + if (fatType_ == 16) return cluster >= FAT16EOC_MIN; + return cluster >= FAT32EOC_MIN; + } + bool readBlock(uint32_t block, uint8_t* dst) { + return sdCard_->readBlock(block, dst);} + bool writeBlock(uint32_t block, const uint8_t* dst) { + return sdCard_->writeBlock(block, dst); + } +//------------------------------------------------------------------------------ + // Deprecated functions - suppress cpplint warnings with NOLINT comment +#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) + + public: + /** \deprecated Use: bool SdVolume::init(Sd2Card* dev); + * \param[in] dev The SD card where the volume is located. + * \return true for success or false for failure. + */ + bool init(Sd2Card& dev) {return init(&dev);} // NOLINT + /** \deprecated Use: bool SdVolume::init(Sd2Card* dev, uint8_t vol); + * \param[in] dev The SD card where the volume is located. + * \param[in] part The partition to be used. + * \return true for success or false for failure. + */ + bool init(Sd2Card& dev, uint8_t part) { // NOLINT + return init(&dev, part); + } +#endif // ALLOW_DEPRECATED_FUNCTIONS +}; +#endif // SdVolume diff --git a/Universal_Serial_Adapter/Libraries/SdFat/bufstream.h b/Universal_Serial_Adapter/Libraries/SdFat/bufstream.h new file mode 100644 index 0000000..8f2123e --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/bufstream.h @@ -0,0 +1,146 @@ +/* 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 bufstream_h +#define bufstream_h +/** + * \file + * \brief \ref ibufstream and \ref obufstream classes + */ +#include +//============================================================================== +/** + * \class ibufstream + * \brief parse a char string + */ +class ibufstream : public istream { + public: + /** Constructor */ + ibufstream() : buf_(0), 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) { + buf_ = str; + len_ = strlen(buf_); + pos_ = 0; + clear(); + } + + protected: + /// @cond SHOW_PROTECTED + int16_t getch() { + if (pos_ < len_) return buf_[pos_++]; + setstate(eofbit); + return -1; + } + void getpos(FatPos_t *pos) { + pos->position = pos_; + } + bool seekoff(off_type off, seekdir way) {return false;} + bool seekpos(pos_type pos) { + if (pos < len_) { + pos_ = pos; + return true; + } + return false; + } + void setpos(FatPos_t *pos) { + pos_ = pos->position; + } + pos_type tellpos() { + return pos_; + } + /// @endcond + private: + const char* buf_; + size_t len_; + size_t pos_; +}; +//============================================================================== +/** + * \class obufstream + * \brief format a char string + */ +class obufstream : public ostream { + public: + /** constructor */ + obufstream() : 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) { + buf_ = buf; + buf[0] = '\0'; + size_ = size; + in_ = 0; + } + /** \return a pointer to the buffer */ + char* buf() {return buf_;} + /** \return the length of the formatted string */ + size_t length() {return in_;} + + protected: + /// @cond SHOW_PROTECTED + void putch(char c) { + if (in_ >= (size_ - 1)) { + setstate(badbit); + return; + } + buf_[in_++] = c; + buf_[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 > in_) return false; + in_ = pos; + buf_[in_] = '\0'; + return true; + } + bool sync() {return true;} + + pos_type tellpos() { + return in_; + } + /// @endcond + private: + char *buf_; + size_t size_; + size_t in_; +}; +#endif // bufstream_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/ios.h b/Universal_Serial_Adapter/Libraries/SdFat/ios.h new file mode 100644 index 0000000..63861d1 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/ios.h @@ -0,0 +1,394 @@ +/* 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 ios_h +#define ios_h +#include +/** + * \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() : fill_(' '), fmtflags_(dec | right | skipws) + , precision_(2), width_(0) {} + /** \return fill character */ + char fill() {return fill_;} + /** Set fill character + * \param[in] c new fill character + * \return old fill character + */ + char fill(char c) { + char r = fill_; + fill_ = c; + return r; + } + /** \return format flags */ + fmtflags flags() const {return fmtflags_;} + /** set format flags + * \param[in] fl new flag + * \return old flags + */ + fmtflags flags(fmtflags fl) { + fmtflags tmp = fmtflags_; + fmtflags_ = fl; + return tmp; + } + /** \return precision */ + int precision() const {return precision_;} + /** set precision + * \param[in] n new precision + * \return old precision + */ + int precision(unsigned int n) { + int r = precision_; + 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 = fmtflags_; + 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 = fmtflags_; + fmtflags_ &= ~mask; + fmtflags_ |= fl; + return r; + } + /** clear format flags + * \param[in] fl flags to be cleared + * \return old flags + */ + void unsetf(fmtflags fl) { + fmtflags_ &= ~fl; + } + /** \return width */ + unsigned width() {return width_;} + /** set width + * \param[in] n new width + * \return old width + */ + unsigned width(unsigned n) { + unsigned r = width_; + 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 fill_; + fmtflags fmtflags_; + unsigned char precision_; + unsigned int 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() : 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 iostate_;} + /** \return True if no iostate flags are set else false. */ + bool good() const {return 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 iostate_ & eofbit;} + /** \return true if any iostate bit other than eof are set else false. */ + bool fail() const {return iostate_ & (failbit | badbit);} + /** \return true if bad bit is set else false. */ + bool bad() const {return iostate_ & badbit;} + /** Clear iostate bits. + * + * \param[in] state The flags you want to set after clearing all flags. + **/ + void clear(iostate state = goodbit) {iostate_ = state;} + /** Set iostate bits. + * + * \param[in] state Bitts to set. + **/ + void setstate(iostate state) {iostate_ |= state;} + + private: + iostate iostate_; +}; +#endif // ios_h diff --git a/Universal_Serial_Adapter/Libraries/SdFat/iostream.h b/Universal_Serial_Adapter/Libraries/SdFat/iostream.h new file mode 100644 index 0000000..6a69f5f --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/iostream.h @@ -0,0 +1,153 @@ +/* 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 iostream_h +#define iostream_h +/** + * \file + * \brief \ref iostream class + */ +#include +#include +/** 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/Universal_Serial_Adapter/Libraries/SdFat/istream.cpp b/Universal_Serial_Adapter/Libraries/SdFat/istream.cpp new file mode 100644 index 0000000..359eec7 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/istream.cpp @@ -0,0 +1,411 @@ +/* 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 +//------------------------------------------------------------------------------ +/** + * 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 istream::get() { + int c; + gcount_ = 0; + c = getch(); + if (c < 0) { + setstate(failbit); + } else { + gcount_ = 1; + } + return c; +} +//------------------------------------------------------------------------------ +/** + * Extract a character if one is available. + * + * \param[out] c location to receive the extracted character. + * + * \return always returns *this. A failure is indicated by the stream state. + */ +istream& istream::get(char& c) { + int tmp = get(); + if (tmp >= 0) c = tmp; + return *this; +} +//------------------------------------------------------------------------------ +/** + * 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& istream::get(char *str, streamsize n, char delim) { + int c; + FatPos_t pos; + gcount_ = 0; + while ((gcount_ + 1) < n) { + c = getch(&pos); + if (c < 0) { + break; + } + if (c == delim) { + setpos(&pos); + break; + } + str[gcount_++] = c; + } + if (n > 0) str[gcount_] = '\0'; + if (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; +} +//------------------------------------------------------------------------------ +/** + * 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& istream::getline(char *str, streamsize n, char delim) { + FatPos_t pos; + int c; + gcount_ = 0; + if (n > 0) str[0] = '\0'; + while (1) { + c = getch(&pos); + if (c < 0) { + break; + } + if (c == delim) { + gcount_++; + break; + } + if ((gcount_ + 1) >= n) { + setpos(&pos); + setstate(failbit); + break; + } + str[gcount_++] = c; + str[gcount_] = '\0'; + } + if (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); +} +//------------------------------------------------------------------------------ +/** + * 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& istream::ignore(streamsize n, int delim) { + int c; + gcount_ = 0; + while (gcount_ < n) { + c = getch(); + if (c < 0) { + break; + } + gcount_++; + if (c == delim) break; + } + return *this; +} +//------------------------------------------------------------------------------ +/** + * Return the next available character without consuming it. + * + * \return The character if the stream state is good else -1; + * + */ +int istream::peek() { + int16_t c; + FatPos_t pos; + 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/Universal_Serial_Adapter/Libraries/SdFat/istream.h b/Universal_Serial_Adapter/Libraries/SdFat/istream.h new file mode 100644 index 0000000..256a5cb --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/istream.h @@ -0,0 +1,306 @@ +/* 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 istream_h +#define istream_h +/** + * \file + * \brief \ref istream class + */ +#include + +/** + * \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 gcount_;} + int get(); + istream& get(char& ch); + istream& get(char *str, streamsize n, char delim = '\n'); + istream& getline(char *str, streamsize count, char delim = '\n'); + istream& ignore(streamsize n = 1, int delim= -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: + size_t gcount_; + 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(); +}; +//------------------------------------------------------------------------------ +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/Universal_Serial_Adapter/Libraries/SdFat/ostream.cpp b/Universal_Serial_Adapter/Libraries/SdFat/ostream.cpp new file mode 100644 index 0000000..d285549 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/ostream.cpp @@ -0,0 +1,176 @@ +/* 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 +#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 in 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/Universal_Serial_Adapter/Libraries/SdFat/ostream.h b/Universal_Serial_Adapter/Libraries/SdFat/ostream.h new file mode 100644 index 0000000..039d65d --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/ostream.h @@ -0,0 +1,287 @@ +/* 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 ostream_h +#define ostream_h +/** + * \file + * \brief \ref ostream class + */ +#include +//------------------------------------------------------------------------------ +/** 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(arg); + return *this; + } + /** Output unsigned long + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (unsigned long arg) { // NOLINT + putNum(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/Universal_Serial_Adapter/Libraries/SdFat/utility/DigitalPin.h b/Universal_Serial_Adapter/Libraries/SdFat/utility/DigitalPin.h new file mode 100644 index 0000000..99a16fa --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/utility/DigitalPin.h @@ -0,0 +1,503 @@ +/* Arduino DigitalPin Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino DigitalPin 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 DigitalPin Library. If not, see + * . + */ +/** + * \file + * \brief DigitalPin class + */ +#ifndef DigitalPin_h +#define DigitalPin_h +#include +#include +//------------------------------------------------------------------------------ +/** DigitalPin version YYYYMMDD */ +#define DIGITAL_PIN_VERSION 20120719 +//------------------------------------------------------------------------------ +/** + * \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 +const static 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_ATmega644P__)\ +|| defined(__AVR_ATmega644__)\ +|| defined(__AVR_ATmega1284P__) +// Sanguino +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 +}; +//------------------------------------------------------------------------------ +#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 + * \return Never called so never returns + */ +uint8_t badPinNumber(void) + __attribute__((error("Pin number is too large or not a constant"))); +//------------------------------------------------------------------------------ +/** 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 fastBitWrite(volatile uint8_t* address, uint8_t bit, bool level) { + if (level) { + *address |= 1 << bit; + } else { + *address &= ~(1 << bit); + } +} +//------------------------------------------------------------------------------ +/** 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 > (uint8_t*)0X5F) { + oldSREG = SREG; + cli(); + } + fastBitWrite(address, bit, level); + if (address > (uint8_t*)0X5F) { + SREG = oldSREG; + } +} +//------------------------------------------------------------------------------ +/** set pin mode + * \param[in] pin Arduino pin number + * \param[in] mode if true set write mode else read mode + */ +static inline __attribute__((always_inline)) + void fastPinMode(uint8_t pin, bool mode) { + if (__builtin_constant_p(pin) && pin < digitalPinCount) { + fastBitWriteSafe(pinMap[pin].ddr, + pinMap[pin].bit, mode); + } else { + badPinNumber(); + } +} +//------------------------------------------------------------------------------ +/** read pin value + * \param[in] pin Arduino pin number + * \return value read + */ +static inline __attribute__((always_inline)) + bool fastDigitalRead(uint8_t pin) { + if (__builtin_constant_p(pin) && pin < digitalPinCount) { + return (*pinMap[pin].pin >> pinMap[pin].bit) & 1; + } else { + return badPinNumber(); + } +} +//------------------------------------------------------------------------------ +/** 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) { + if (__builtin_constant_p(pin) && pin < digitalPinCount) { + fastBitWriteSafe(pinMap[pin].port, pinMap[pin].bit, level); + } else { + badPinNumber(); + } +} +//------------------------------------------------------------------------------ +/** Set pin value in ISR + * \param[in] pin Arduino pin number + * \param[in] level value to write + */ +static inline __attribute__((always_inline)) + void fastDigitalWriteISR(uint8_t pin, bool level) { + if (__builtin_constant_p(pin) && pin < digitalPinCount) { + fastBitWrite(pinMap[pin].port, pinMap[pin].bit, level); + } else { + badPinNumber(); + } +} +//============================================================================== +/** + * \class DigitalPin + * \brief digital avr port I/O + */ +template +class DigitalPin { + public: + //---------------------------------------------------------------------------- + /** + * Set pin level high if output mode or enable 20K pullup if input mode. + */ + void high() {write(true);} + //---------------------------------------------------------------------------- + /** Set the pin mode to input. */ + void inputMode() { + if ((uint16_t)pinMap[PinNumber].ddr > 0X5F) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + isrInputMode(); + } + } else { + isrInputMode(); + } + } + //---------------------------------------------------------------------------- + /** Set the pin mode to input. + * \param[in] pullup If true enable the pin's pullup else disable the pullup. + */ + void inputMode(bool pullup) { + if ((uint16_t)pinMap[PinNumber].ddr > 0X5F) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + isrInputMode(pullup); + } + } else { + isrInputMode(pullup); + } + } + //---------------------------------------------------------------------------- + /** + * Set pin level low if output mode or disable 20K pullup if input mode. + */ + void low() {write(false);} + //---------------------------------------------------------------------------- + /** Set the pin's mode to output */ + void outputMode() { + if ((uint16_t)pinMap[PinNumber].ddr > 0X5F) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + isrOutputMode(); + } + } else { + isrOutputMode(); + } + } + //---------------------------------------------------------------------------- + /** \return Pin's level */ + bool read() { + return *pinMap[PinNumber].pin & (1 << pinMap[PinNumber].bit); + } + //---------------------------------------------------------------------------- + /** Write the pin's level. + * \param[in] value If true set the pin's level high else set the + * pin's level low. + */ + void write(bool value) { + if ((uint16_t)pinMap[PinNumber].port > 0X5F) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + isrWrite(value); + } + } else { + isrWrite(value); + } + } + private: + //---------------------------------------------------------------------------- + void isrHigh() { + *pinMap[PinNumber].port |= 1 << pinMap[PinNumber].bit; + } + //---------------------------------------------------------------------------- + void isrInputMode() { + *pinMap[PinNumber].ddr &= ~(1 << pinMap[PinNumber].bit); + } + //---------------------------------------------------------------------------- + void isrInputMode(bool pullup) { + isrInputMode(); + isrWrite(pullup); + } + //---------------------------------------------------------------------------- + void isrLow() { + *pinMap[PinNumber].port &= ~(1 << pinMap[PinNumber].bit); + } + //---------------------------------------------------------------------------- + void isrOutputMode() { + *pinMap[PinNumber].ddr |= 1 << pinMap[PinNumber].bit; + } + //---------------------------------------------------------------------------- + void isrWrite(bool value) { + if (value) { + isrHigh(); + } else { + isrLow(); + } + } +}; +#endif // DigitalPin_h \ No newline at end of file diff --git a/Universal_Serial_Adapter/Libraries/SdFat/utility/SoftSPI.h b/Universal_Serial_Adapter/Libraries/SdFat/utility/SoftSPI.h new file mode 100644 index 0000000..0562e9e --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SdFat/utility/SoftSPI.h @@ -0,0 +1,120 @@ +#ifndef SoftSPI_h +#define SoftSPI_h +#include + +/** nop for timing */ +#define nop asm volatile ("nop\n\t") +//------------------------------------------------------------------------------ +/** + * \class SoftSPI + * \brief fast bit-bang SPI + */ +template +class SoftSPI { + public: + //----------------------------------------------------------------------------- + /** initialize SoftSpi */ + void begin() { + fastPinMode(MisoPin, false); + fastPinMode(MosiPin, true); + fastPinMode(SckPin, true); + fastDigitalWrite(SckPin, MODE_CPOL(Mode)); + } + //---------------------------------------------------------------------------- + /** Soft SPI receive byte + * \return byte received + */ + 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 byte to send + */ + 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 byte to send + * \return 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 \ No newline at end of file diff --git a/Universal_Serial_Adapter/Libraries/SerialPort/SerialPort.cpp b/Universal_Serial_Adapter/Libraries/SerialPort/SerialPort.cpp new file mode 100644 index 0000000..909f641 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SerialPort/SerialPort.cpp @@ -0,0 +1,297 @@ +/* Arduino SerialPort Library + * Copyright (C) 2011 by William Greiman + * + * This file is part of the Arduino SerialPort 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 SerialPort Library. If not, see + * . + */ +/** + * @file + * @brief Serial Port class + */ +#include +//------------------------------------------------------------------------------ +/** @return The number of bytes in the ring buffer. + * + * @note This function must not be called with interrupts disabled. + */ +int SerialRingBuffer::available() { + uint8_t s = SREG; + cli(); + int n = head_ - tail_; + SREG = s; + return n < 0 ? size_ + n : n; +} +//------------------------------------------------------------------------------ +/** Discard all data in the ring buffer. + * + * @note This function must not be called with interrupts disabled. + */ +void SerialRingBuffer::flush() { + uint8_t s = SREG; + cli(); + head_ = tail_ = 0; + SREG = s; +} +//------------------------------------------------------------------------------ +/** Get the next byte from the ring buffer. + * + * @param[in] b location for the returned byte + * @return @c true if a byte was returned or @c false if the ring buffer is empty + */ +bool SerialRingBuffer::get(uint8_t* b) { + buf_size_t t = tail_; + if (head_ == t) return false; + *b = buf_[t++]; + tail_ = t < size_ ? t : 0; + return true; +} +//------------------------------------------------------------------------------ +/** + * Get the maximum number of contiguous bytes from the ring buffer + * with one call to memcpy. + * + * @note This function must not be called with interrupts disabled. + * + * @param[in] b Pointer to the data. + * @param[in] n Number of bytes to transfer from the ring buffer. + * @return Number of bytes transferred. + */ +SerialRingBuffer::buf_size_t SerialRingBuffer::get(uint8_t* b, buf_size_t n) { + buf_size_t nr; + cli(); + buf_size_t h = head_; + sei(); + buf_size_t t = tail_; + if (h < t) { + nr = size_ - t; + } else if (t < h) { + nr = h - t; + } else { + return 0; + } + if (nr > n) nr = n; + memcpy(b, &buf_[t], nr); + t += nr; + tail_ = t < size_ ? t : t - size_; + return nr; +} +//------------------------------------------------------------------------------ +/** Initialize the ring buffer. + * @param[in] b Buffer for the data. + * @param[in] s Size of the buffer. + */ +void SerialRingBuffer::init(uint8_t* b, buf_size_t s) { + buf_ = b; + size_ = s; + head_ = tail_ = 0; +} +//------------------------------------------------------------------------------ +/** Peek at the next byte in the ring buffer. + * @return The next byte that would be read or -1 if the ring buffer is empty. + */ +int SerialRingBuffer::peek() { + return empty() ? -1 : buf_[tail_]; +} +//------------------------------------------------------------------------------ +/** Put a byte into the ring buffer. + * + * @param[in] b the byte + * @return @c true if byte was transferred or + * @c false if the ring buffer is full. + */ +bool SerialRingBuffer::put(uint8_t b) { + buf_size_t h = head_; + // OK to store here even if ring is full. + buf_[h++] = b; + if (h >= size_) h = 0; + if (h == tail_) return false; + head_ = h; + return true; +} +//------------------------------------------------------------------------------ +/** + * Put the maximum number of contiguous bytes into the ring buffer. + * with one call to memcpy. + * + * @note This function must not be called with interrupts disabled. + * + * @param[in] b pointer to data. + * @param[in] n number of bytes to transfer to the ring buffer. + * @return number of bytes transferred. + */ +SerialRingBuffer::buf_size_t + SerialRingBuffer::put(const uint8_t* b, buf_size_t n) { + cli(); + buf_size_t t = tail_; + sei(); + buf_size_t space; // space in ring buffer + buf_size_t h = head_; + if (h < t) { + space = t - h - 1; + } else { + space = size_ - h; + if (t == 0) space -= 1; + } + if (n > space) n = space; + memcpy(&buf_[h], b, n); + h += n; + head_ = h < size_ ? h : h - size_; + return n; +} +//------------------------------------------------------------------------------ +/** + * Put the maximum number of contiguous bytes into the ring buffer. + * with one call to memcpy. + * + * @note This function must not be called with interrupts disabled. + * + * @param[in] b pointer to data. + * @param[in] n number of bytes to transfer to the ring buffer. + * @return number of bytes transferred. + */ +SerialRingBuffer::buf_size_t SerialRingBuffer::put_P(PGM_P b, buf_size_t n) { + cli(); + buf_size_t t = tail_; + sei(); + buf_size_t space; // space in ring buffer + buf_size_t h = head_; + if (h < t) { + space = t - h - 1; + } else { + space = size_ - h; + if (t == 0) space -= 1; + } + if (n > space) n = space; + memcpy_P(&buf_[h], b, n); + h += n; + head_ = h < size_ ? h : h - size_; + return n; +} +//============================================================================== +// global data and ISRs +#if ENABLE_RX_ERROR_CHECKING +// +uint8_t rxErrorBits[SERIAL_PORT_COUNT]; +#endif // ENABLE_RX_ERROR_CHECKING +//------------------------------------------------------------------------------ +#if BUFFERED_RX +//------------------------------------------------------------------------------ +SerialRingBuffer rxRingBuf[SERIAL_PORT_COUNT]; +//------------------------------------------------------------------------------ +#if ENABLE_RX_ERROR_CHECKING +inline static void rx_isr(uint8_t n) { + uint8_t e = *usart[n].ucsra & SP_UCSRA_ERROR_MASK; + uint8_t b = *usart[n].udr; + if (!rxRingBuf[n].put(b)) e |= SP_RX_BUF_OVERRUN; + rxErrorBits[n] |= e; +} +#else // ENABLE_RX_ERROR_CHECKING +inline static void rx_isr(uint8_t n) { + uint8_t b = *usart[n].udr; + rxRingBuf[n].put(b); +} +#endif // ENABLE_RX_ERROR_CHECKING +//------------------------------------------------------------------------------ +// SerialRingBuffer rxbuf0; +#if defined(USART_RX_vect) +ISR(USART_RX_vect) { + rx_isr(0); +} +#elif defined(SIG_USART0_RECV) +ISR(SIG_USART0_RECV) { + rx_isr(0); +} +#elif defined(SIG_UART0_RECV) +ISR(SIG_UART0_RECV) { + rx_isr(0); +} +#elif defined(USART0_RX_vect) +ISR(USART0_RX_vect) { + rx_isr(0); +} +#elif defined(SIG_UART_RECV) +ISR(SIG_UART_RECV) { + rx_isr(0); +} +#endif // vector USART0 +#ifdef USART1_RX_vect +ISR(USART1_RX_vect) { + rx_isr(1); +} +#endif // USART1_RX_vect + +#ifdef USART2_RX_vect +ISR(USART2_RX_vect) { + rx_isr(2); +} +#endif // USART2_RX_vect + +#ifdef USART3_RX_vect +ISR(USART3_RX_vect) { + rx_isr(3); +} +#endif // USART3_RX_vect +#endif // BUFFERED_RX +//------------------------------------------------------------------------------ +#if BUFFERED_TX +//------------------------------------------------------------------------------ +SerialRingBuffer txRingBuf[SERIAL_PORT_COUNT]; +//------------------------------------------------------------------------------ +inline static void tx_isr(uint8_t n) { + uint8_t b; + if (txRingBuf[n].get(&b)) { + *usart[n].udr = b; + } else { + // no data - disable interrupts + *usart[n].ucsrb &= ~M_UDRIE; + } +} +#if defined(UART0_UDRE_vect) +ISR(UART0_UDRE_vect) { + tx_isr(0); +} +#elif defined(UART_UDRE_vect) +ISR(UART_UDRE_vect) { + tx_isr(0); +} +#elif defined(USART0_UDRE_vect) +ISR(USART0_UDRE_vect) { + tx_isr(0); +} +#elif defined(USART_UDRE_vect) +ISR(USART_UDRE_vect) { + tx_isr(0); +} +#endif // USART0 TX + +#ifdef USART1_UDRE_vect +ISR(USART1_UDRE_vect) { + tx_isr(1); +} +#endif // USART1_UDRE_vect + +#ifdef USART2_UDRE_vect +ISR(USART2_UDRE_vect) { + tx_isr(2); +} +#endif // USART2_UDRE_vect + +#ifdef USART3_UDRE_vect +ISR(USART3_UDRE_vect) { + tx_isr(3); +} +#endif // USART3_UDRE_vect +#endif // BUFFERED_TX diff --git a/Universal_Serial_Adapter/Libraries/SerialPort/SerialPort.h b/Universal_Serial_Adapter/Libraries/SerialPort/SerialPort.h new file mode 100644 index 0000000..ec6dff3 --- /dev/null +++ b/Universal_Serial_Adapter/Libraries/SerialPort/SerialPort.h @@ -0,0 +1,635 @@ +/* Arduino SerialPort Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SerialPort 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 SerialPort Library. If not, see + * . + */ +/** + * @file + * @brief Serial Port class + */ +#ifndef SerialPort_h +#define SerialPort_h +//------------------------------------------------------------------------------ +/** SerialPort version YYYYMMDD */ +#define SERIAL_PORT_VERSION 20130222 +//------------------------------------------------------------------------------ +/** + * Set ALLOW_LARGE_BUFFERS to zero to limit buffer sizes to 254 bytes. + * + * ALLOW_LARGE_BUFFERS controls whether uint16_t or uint8_t will be + * used for buffer indices. + */ +#define ALLOW_LARGE_BUFFERS 1 +//------------------------------------------------------------------------------ +/** + * Set USE_WRITE_OVERRIDES to zero to use the Arduino Print version + * of write(const char*) and write(const uint8_t*, size_t). + * + * This will save some flash but is much slower. + */ +#define USE_WRITE_OVERRIDES 1 +//------------------------------------------------------------------------------ +/** + * Set BUFFERED_RX zero to save flash and RAM if no RX buffering is used. + * + * RxBufSize must be zero in all SerialPort constructors if + * BUFFERED_RX is zero. + */ +#define BUFFERED_RX 1 +//------------------------------------------------------------------------------ +/** + * Set BUFFERED_TX zero to save flash and RAM if no TX buffering is used. + * + * TxBufSize must be zero in all SerialPort constructors if + * BUFFERED_TX is zero. + */ +#define BUFFERED_TX 1 +//------------------------------------------------------------------------------ +/** + * Set ENABLE_RX_ERROR_CHECKING zero to disable RX error checking. + */ +#define ENABLE_RX_ERROR_CHECKING 1 +//------------------------------------------------------------------------------ +// Define symbols to allocate 64 byte ring buffers with capacity for 63 bytes. +/** Define NewSerial with buffering like Arduino 1.0. */ +#define USE_NEW_SERIAL SerialPort<0, 63, 63> NewSerial +/** Define NewSerial1 with buffering like Arduino 1.0. */ +#define USE_NEW_SERIAL1 SerialPort<1, 63, 63> NewSerial1 +/** Define NewSerial2 with buffering like Arduino 1.0. */ +#define USE_NEW_SERIAL2 SerialPort<2, 63, 63> NewSerial2 +/** Define NewSerial3 with buffering like Arduino 1.0. */ +#define USE_NEW_SERIAL3 SerialPort<3, 63, 63> NewSerial3 +//------------------------------------------------------------------------------ +#include +#include +#include +//------------------------------------------------------------------------------ +#if defined(UCSR3A) +static const uint8_t SERIAL_PORT_COUNT = 4; +#elif defined(UCSR2A) +static const uint8_t SERIAL_PORT_COUNT = 3; +#elif defined(UCSR1A) +static const uint8_t SERIAL_PORT_COUNT = 2; +#elif defined(UCSR0A) || defined(UCSRA) +static const uint8_t SERIAL_PORT_COUNT = 1; +#else +#error no serial ports. +#endif +//------------------------------------------------------------------------------ +#ifdef UCSR0A +// Bits in UCSRA. +static const uint8_t M_RXC = 1 << RXC0; +static const uint8_t M_TXC = 1 << TXC0; +static const uint8_t M_UDRE = 1 << UDRE0; +static const uint8_t M_FE = 1 << FE0; +static const uint8_t M_DOR = 1 << DOR0; +static const uint8_t M_UPE = 1 << UPE0; +static const uint8_t M_U2X = 1 << U2X0; +// Bits in UCSRB. +static const uint8_t M_RXCIE = 1 << RXCIE0; +static const uint8_t M_TXCIE = 1 << TXCIE0; +static const uint8_t M_UDRIE = 1 << UDRIE0; +static const uint8_t M_RXEN = 1 << RXEN0; +static const uint8_t M_TXEN = 1 << TXEN0; +// Bits in UCSRC. +static const uint8_t M_UPM0 = 1 << UPM00; +static const uint8_t M_UPM1 = 1 << UPM01; +static const uint8_t M_USBS = 1 << USBS0; +static const uint8_t M_UCSZ0 = 1 << UCSZ00; +static const uint8_t M_UCSZ1 = 1 << UCSZ01; +#elif defined(UCSRA) // UCSR0A +// Bits in UCSRA. +static const uint8_t M_RXC = 1 << RXC; +static const uint8_t M_TXC = 1 << TXC; +static const uint8_t M_UDRE = 1 << UDRE; +static const uint8_t M_FE = 1 << FE; +static const uint8_t M_DOR = 1 << DOR; +static const uint8_t M_UPE = 1 << PE; +static const uint8_t M_U2X = 1 << U2X; +// Bits in UCSRB. +static const uint8_t M_RXCIE = 1 << RXCIE; +static const uint8_t M_TXCIE = 1 << TXCIE; +static const uint8_t M_UDRIE = 1 << UDRIE; +static const uint8_t M_RXEN = 1 << RXEN; +static const uint8_t M_TXEN = 1 << TXEN; +// Bits in UCSRC. +static const uint8_t M_UPM0 = 1 << UPM0; +static const uint8_t M_UPM1 = 1 << UPM1; +static const uint8_t M_USBS = 1 << USBS; +static const uint8_t M_UCSZ0 = 1 << UCSZ0; +static const uint8_t M_UCSZ1 = 1 << UCSZ1; +#elif defined(UCSR1A) // UCSR0A +// Bits in UCSRA. +static const uint8_t M_RXC = 1 << RXC1; +static const uint8_t M_TXC = 1 << TXC1; +static const uint8_t M_UDRE = 1 << UDRE1; +static const uint8_t M_FE = 1 << FE1; +static const uint8_t M_DOR = 1 << DOR1; +static const uint8_t M_UPE = 1 << UPE1; +static const uint8_t M_U2X = 1 << U2X1; +// Bits in UCSRB. +static const uint8_t M_RXCIE = 1 << RXCIE1; +static const uint8_t M_TXCIE = 1 << TXCIE1; +static const uint8_t M_UDRIE = 1 << UDRIE1; +static const uint8_t M_RXEN = 1 << RXEN1; +static const uint8_t M_TXEN = 1 << TXEN1; +// Bits in UCSRC. +static const uint8_t M_UPM0 = 1 << UPM10; +static const uint8_t M_UPM1 = 1 << UPM11; +static const uint8_t M_USBS = 1 << USBS1; +static const uint8_t M_UCSZ0 = 1 << UCSZ10; +static const uint8_t M_UCSZ1 = 1 << UCSZ11; +#else // UCSR0A +#error no serial ports +#endif // UCSR0A +//------------------------------------------------------------------------------ +/** Use one stop bit. */ +static const uint8_t SP_1_STOP_BIT = 0; +/** Use two stop bits. */ +static const uint8_t SP_2_STOP_BIT = M_USBS; + +/** No parity bit. */ +static const uint8_t SP_NO_PARITY = 0; +/** Use even parity. */ +static const uint8_t SP_EVEN_PARITY = M_UPM1; +/** Use odd parity. */ +static const uint8_t SP_ODD_PARITY = M_UPM0 | M_UPM1; + +/** Use 5-bit character size. */ +static const uint8_t SP_5_BIT_CHAR = 0; +/** Use 6-bit character size. */ +static const uint8_t SP_6_BIT_CHAR = M_UCSZ0; +/** Use 7-bit character size. */ +static const uint8_t SP_7_BIT_CHAR = M_UCSZ1; +/** Use 8-bit character size. */ +static const uint8_t SP_8_BIT_CHAR = M_UCSZ0 | M_UCSZ1; +/** Mask for all options bits. */ +static const uint8_t SP_OPT_MASK = M_USBS | M_UPM0 | M_UPM1 |M_UCSZ0 | M_UCSZ1; + +/** USART framing error bit. */ +static const uint8_t SP_FRAMING_ERROR = M_FE; +/** USART RX data overrun error bit. */ +static const uint8_t SP_RX_DATA_OVERRUN = M_DOR; +/** USART parity error bit. */ +static const uint8_t SP_PARITY_ERROR = M_UPE; +/** Mask for all error bits in UCSRA. */ +static const uint8_t SP_UCSRA_ERROR_MASK = M_FE | M_DOR | M_UPE; +/** RX ring buffer full overrun. */ +static const uint8_t SP_RX_BUF_OVERRUN = 1; +#if 1 & ((1 << FE0) | (1 << DOR0) |(1 << UPE0)) +#error Invalid SP_RX_BUF_OVERRUN bit +#endif // SP_RX_BUF_OVERRUN +//------------------------------------------------------------------------------ +/** + * @class UsartRegister + * @brief Addresses of USART registers. + */ +struct UsartRegister { + volatile uint8_t* ucsra; /**< USART Control and Status Register A. */ + volatile uint8_t* ucsrb; /**< USART Control and Status Register B. */ + volatile uint8_t* ucsrc; /**< USART Control and Status Register C. */ + volatile uint8_t* ubrrl; /**< USART Baud Rate Register Low. */ + volatile uint8_t* ubrrh; /**< USART Baud Rate Register High. */ + volatile uint8_t* udr; /**< USART I/O Data Register. */ +}; +//------------------------------------------------------------------------------ +/** + * Pointers to USART registers. This static const array allows the compiler + * to generate very efficient code if the array index is a constant. + */ +static const UsartRegister usart[] = { +#ifdef UCSR0A + {&UCSR0A, &UCSR0B, &UCSR0C, &UBRR0L, &UBRR0H, &UDR0}, +#elif defined(UCSRA) + {&UCSRA, &UCSRB, &UCSRC, &UBRRL, &UBRRH, &UDR}, +#else // UCSR0A + {0, 0, 0, 0, 0, 0}, +#endif // UCSR0A + +#ifdef UCSR1A + {&UCSR1A, &UCSR1B, &UCSR1C, &UBRR1L, &UBRR1H, &UDR1}, +#else // UCSR1A + {0, 0, 0, 0, 0, 0}, +#endif // UCSR1A + +#ifdef UCSR2A + {&UCSR2A, &UCSR2B, &UCSR2C, &UBRR2L, &UBRR2H, &UDR2}, +#else // UCSR2A + {0, 0, 0, 0, 0, 0}, +#endif // UCSR2A + +#ifdef UCSR3A + {&UCSR3A, &UCSR3B, &UCSR3C, &UBRR3L, &UBRR3H, &UDR3} +#else // UCSR3A + {0, 0, 0, 0, 0, 0} +#endif // UCSR3A +}; +//------------------------------------------------------------------------------ +/** + * @class SerialRingBuffer + * @brief Ring buffer for RX and TX data. + */ +class SerialRingBuffer { + public: + /** Define type for buffer indices */ +#if ALLOW_LARGE_BUFFERS + typedef uint16_t buf_size_t; +#else // ALLOW_LARGE_BUFFERS + typedef uint8_t buf_size_t; +#endif // ALLOW_LARGE_BUFFERS + int available(); + /** @return @c true if the ring buffer is empty else @c false. */ + bool empty() {return head_ == tail_;} + void flush(); + bool get(uint8_t* b); + buf_size_t get(uint8_t* b, buf_size_t n); + void init(uint8_t* b, buf_size_t s); + int peek(); + bool put(uint8_t b); + buf_size_t put(const uint8_t* b, buf_size_t n); + buf_size_t put_P(PGM_P b, buf_size_t n); + private: + uint8_t* buf_; /**< Pointer to start of buffer. */ + volatile buf_size_t head_; /**< Index to next empty location. */ + volatile buf_size_t tail_; /**< Index to last entry if head_ != tail_. */ + buf_size_t size_; /**< Size of the buffer. Capacity is size -1. */ +}; +//------------------------------------------------------------------------------ +/** RX ring buffers. */ +extern SerialRingBuffer rxRingBuf[]; +/** TX ring buffers. */ +extern SerialRingBuffer txRingBuf[]; +/** RX error bits. */ +extern uint8_t rxErrorBits[]; +//------------------------------------------------------------------------------ +/** Cause error message for bad port number. + * @return Never returns since it is never called. + */ +uint8_t badPortNumber(void) + __attribute__((error("Bad port number"))); +/** Cause error message for bad port number. + * @return Never returns since it is never called. + */ +uint8_t badRxBufSize(void) + __attribute__((error("RX buffer size too large"))); +/** Cause error message for bad port number. + * @return Never returns since it is never called. + */ +uint8_t badTxBufSize(void) + __attribute__((error("TX buffer size too large"))); +//------------------------------------------------------------------------------ +/** + * @class SerialPort + * @brief Class for avr hardware USART ports. + */ +template +class SerialPort : public Stream { + public: + //---------------------------------------------------------------------------- + /** Constructor */ + SerialPort() { + if (PortNumber >= SERIAL_PORT_COUNT || !usart[PortNumber].ucsra) { + badPortNumber(); + } + if (sizeof(SerialRingBuffer::buf_size_t) == 1) { + if (RxBufSize > 254) badRxBufSize(); + if (TxBufSize > 254) badTxBufSize(); + } + if (RxBufSize) rxRingBuf[PortNumber].init(rxBuffer_, sizeof(rxBuffer_)); + if (TxBufSize) txRingBuf[PortNumber].init(txBuffer_, sizeof(txBuffer_)); + } + //---------------------------------------------------------------------------- + /** + * @return The number of bytes (characters) available for reading from + * the serial port. + */ + int available(void) { + if (!RxBufSize) { + return *usart[PortNumber].ucsra & M_RXC ? 1 : 0; + } else { + return rxRingBuf[PortNumber].available(); + } + } + //---------------------------------------------------------------------------- + /** + * Sets the data rate in bits per second (baud) for serial data transmission. + * + * @param[in] baud Rate in bits per second (baud). + * @param[in] options constructed by a bitwise-inclusive + * OR of values from the following list. Choose one value for stop bit, + * parity, and character size. + * + * - SP_1_STOP_BIT - Use one stop bit (default if stop bit not specified). + * - SP_2_STOP_BIT - Use two stop bits. + * - SP_NO_PARITY - No parity bit (default if parity not specified). + * - SP_EVEN_PARITY - Add even parity bit. + * - SP_ODD_PARITY - Add odd parity bit. + * - SP_5_BIT_CHAR - Use 5-bit characters (default if size not specified). + * - SP_6_BIT_CHAR - Use 6-bit characters. + * - SP_7_BIT_CHAR - Use 7-bit characters. + * - SP_8_BIT_CHAR - Use 8-bit characters. + * . + * The default is SP_8_BIT_CHAR which results in one stop bit, no parity, + * and 8-bit characters. + */ + void begin(uint32_t baud, uint8_t options = SP_8_BIT_CHAR) { + uint16_t baud_setting; + + // disable USART interrupts. Set UCSRB to reset values. + *usart[PortNumber].ucsrb = 0; + + // set option bits + *usart[PortNumber].ucsrc = options & SP_OPT_MASK; + + if (F_CPU == 16000000UL && baud == 57600) { + // 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. + *usart[PortNumber].ucsra = 0; + baud_setting = (F_CPU / 8 / baud - 1) / 2; + } else { + *usart[PortNumber].ucsra = M_U2X; + baud_setting = (F_CPU / 4 / baud - 1) / 2; + } + // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) + *usart[PortNumber].ubrrh = baud_setting >> 8; + *usart[PortNumber].ubrrl = baud_setting; + + // enable RX and TX + uint8_t bits = M_TXEN | M_RXEN; + + // enable receive interrupt if buffered + if (RxBufSize) bits |= M_RXCIE; + *usart[PortNumber].ucsrb = bits; + } + //---------------------------------------------------------------------------- + #if ENABLE_RX_ERROR_CHECKING + /** Clear RX error bits. */ + void clearRxError() {rxErrorBits[PortNumber] = 0;} + /** @return RX error bits. Possible error bits are: + * - @ref SP_RX_BUF_OVERRUN + * - @ref SP_RX_DATA_OVERRUN + * - @ref SP_FRAMING_ERROR + * - @ref SP_PARITY_ERROR + * . + */ + uint8_t getRxError() {return rxErrorBits[PortNumber];} + #endif // ENABLE_RX_ERROR_CHECKING + //---------------------------------------------------------------------------- + /** + * Disables serial communication, allowing the RX and TX pins to be used for + * general input and output. To re-enable serial communication, + * call SerialPort::begin(). + */ + void end() { + // wait for transmission of outgoing data + flushTx(); + // disable USART + cli(); + *usart[PortNumber].ucsrb &= ~(M_RXEN | M_TXEN | M_RXCIE | M_UDRIE); + sei(); + // clear any received data + flushRx(); + } + //---------------------------------------------------------------------------- + /** + * For Arduino 1.0 and greater call flushTx(). + */ + void flush() {flushTx();} + //---------------------------------------------------------------------------- + /** + * Discard any buffered incoming serial data. + */ + void flushRx() { + if (RxBufSize) { + rxRingBuf[PortNumber].flush(); + } else { + uint8_t b; + while (*usart[PortNumber].ucsra & M_RXC) b = *usart[PortNumber].udr; + } + } + //---------------------------------------------------------------------------- + /** + * Waits for the transmission of outgoing serial data to complete. + */ + void flushTx() { + if (TxBufSize) { + while (!txRingBuf[PortNumber].empty()) {} + } + } + //---------------------------------------------------------------------------- + /** + * @return The first byte of incoming serial data available or + * -1 if no data is available. -1 is always returned for unbuffered RX. + */ + int peek(void) { + return RxBufSize ? rxRingBuf[PortNumber].peek() : -1; + } + //---------------------------------------------------------------------------- + /** + * Read incoming serial data. + * + * @return The first byte of incoming serial data available + * or -1 if no data is available. + */ + __attribute__((noinline)) + int read() { + if (!RxBufSize) { + uint8_t s = *usart[PortNumber].ucsra; + #if ENABLE_RX_ERROR_CHECKING + rxErrorBits[PortNumber] |= s & SP_UCSRA_ERROR_MASK; + #endif // ENABLE_RX_ERROR_CHECKING + return s & M_RXC ? *usart[PortNumber].udr : -1; + } else { + uint8_t b; + return rxRingBuf[PortNumber].get(&b) ? b : -1; + } + } + //---------------------------------------------------------------------------- + /** + * Read incoming serial data. Stop when RX buffer is empty or n + * bytes have been read. + * + * @param[in] b The location to receive the data. + * @param[in] n Maximum number of bytes to read. + * @return The number of bytes read. + */ + __attribute__((noinline)) + size_t read(uint8_t* b, size_t n) { + uint8_t* limit = b + n; + uint8_t* p = b; + if (RxBufSize) { + while (p < limit && !rxRingBuf[PortNumber].empty()) { + size_t nr = limit - p; + if (sizeof(SerialRingBuffer::buf_size_t) == 1 && nr > 255) nr = 255; + p += rxRingBuf[PortNumber].get(p, nr); + } + } else { + while (p < limit) { + int rb = read(); + if (rb < 0) break; + *p++ = rb; + } + } + return p - b; + } + //---------------------------------------------------------------------------- + /** + * Write binary data to the serial port. + * + * @param[in] b The byte to be written. + * @return The number of bytes written to the serial port. + */ + __attribute__((noinline)) + size_t write(uint8_t b) { + if (!TxBufSize) { + while (!(*usart[PortNumber].ucsra & M_UDRE)) {} + *usart[PortNumber].udr = b; + } else { + // Wait for TX ISR if buffer is full. + while (!txRingBuf[PortNumber].put(b)) {} + + // Enable interrupts. + *usart[PortNumber].ucsrb |= M_UDRIE; + } + return 1; + } + //---------------------------------------------------------------------------- + /** Write CR/LF. + * @return 2 + */ + __attribute__((noinline)) + size_t writeln() { + write('\r'); + write('\n'); + return 2; + } + //---------------------------------------------------------------------------- + /** + * Write a string to the serial port followed by CR/LF. + * + * @param[in] s The string to be written. + * @return The number of bytes written to the serial port. + */ + __attribute__((noinline)) + size_t writeln(const char* s) { + return write(s) + writeln(); + } + + //---------------------------------------------------------------------------- + /** + * Write binary data from flash memory to the serial port. + * + * @param[in] b Location of the bytes to be written. + * @param[in] n The number of bytes to write. + * @return The number of bytes written to the serial port. + */ + __attribute__((noinline)) + size_t write_P(PGM_P b, size_t n) { + if (!TxBufSize) { + for (size_t i = 0; i < n; i++) write(pgm_read_byte(b + i)); + } else { + size_t w = n; + while (w) { + size_t nw = w; + if (sizeof(SerialRingBuffer::buf_size_t) == 1 && nw > 255) nw = 255; + size_t m = txRingBuf[PortNumber].put_P(b, nw); + + // enable interrupts + *usart[PortNumber].ucsrb |= M_UDRIE; + w -= m; + b += m; + } + } + return n; + } + //---------------------------------------------------------------------------- + /** + * Write a flash string to the serial port. + * + * @param[in] s The string to be written. + * @return The number of bytes written to the serial port. + */ + __attribute__((noinline)) + size_t write(const __FlashStringHelper* s) { + const char PROGMEM* p = (const char PROGMEM*)s; + size_t n = strlen_P(p); + return write_P(p, n); + } + //---------------------------------------------------------------------------- + /** + * Write a flash string to the serial port followed by CR/LF. + * + * @param[in] s The string to be written. + * @return The number of bytes written to the serial port. + */ + __attribute__((noinline)) + size_t writeln(const __FlashStringHelper* s) { + return write(s) + writeln(); + } + #if USE_WRITE_OVERRIDES + //---------------------------------------------------------------------------- + /** + * Write binary data to the serial port. + * + * @param[in] b Location of the bytes to be written. + * @param[in] n The number of bytes to write. + * @return The number of bytes written to the serial port. + */ + __attribute__((noinline)) + size_t write(const uint8_t* b, size_t n) { + if (!TxBufSize) { + for (size_t i = 0; i < n; i++) write(b[i]); + } else { + size_t w = n; + while (w) { + size_t nw = w; + if (sizeof(SerialRingBuffer::buf_size_t) == 1 && nw > 255) nw = 255; + size_t m = txRingBuf[PortNumber].put(b, nw); + + // Enable interrupts. + *usart[PortNumber].ucsrb |= M_UDRIE; + w -= m; + b += m; + } + } + return n; + } + //---------------------------------------------------------------------------- + /** + * Write a string to the serial port. + * + * @param[in] s The string to be written. + * @return The number of bytes written to the serial port + */ + __attribute__((noinline)) + size_t write(const char* s) { + size_t n = strlen(s); + return write(reinterpret_cast(s), n); + } + #else // USE_WRITE_OVERRIDES + using Print::write; // use write(str) and write(buf, size) from Print + #endif // USE_WRITE_OVERRIDES + //---------------------------------------------------------------------------- + private: + // RX buffer with a capacity of RxBufSize. + uint8_t rxBuffer_[RxBufSize + 1]; + // TX buffer with a capacity of TxBufSize. + uint8_t txBuffer_[TxBufSize + 1]; +}; +//------------------------------------------------------------------------------ +#endif // SerialPort_h diff --git a/Universal_Serial_Adapter/Project.h b/Universal_Serial_Adapter/Project.h index f9aeafd..4f0706f 100644 --- a/Universal_Serial_Adapter/Project.h +++ b/Universal_Serial_Adapter/Project.h @@ -8,6 +8,8 @@ attribute. */ +#include + #ifndef Project_h #define Project_h @@ -51,6 +53,12 @@ #define FONT_WIDTH 6 #define FONT_HEIGHT 8 +// Serial ports +extern SerialPort<0, 4096, 0> serialPort0; +extern SerialPort<0, 4096, 0> serialPort1; +extern SerialPort<0, 4096, 0> serialPort2; +extern SerialPort<0, 4096, 0> serialPort3; + // Serial modes supported // Abused in for loops / lookup tables -- DO NOT CHANGE none or set values enum serialmode { diff --git a/Universal_Serial_Adapter/UI.cpp b/Universal_Serial_Adapter/UI.cpp index 6274fb6..f0896e1 100644 --- a/Universal_Serial_Adapter/UI.cpp +++ b/Universal_Serial_Adapter/UI.cpp @@ -15,7 +15,7 @@ UI::UI() { #if DEBUG == 2 - Serial.println("Config::UI()"); + serialPort0.println("Config::UI()"); #endif okButton = new UIButton(okButtonPin, okButtonLed); @@ -61,7 +61,7 @@ void UI::resetTimeout() { void UI::startUI() { #if DEBUG == 2 - Serial.println("Config::startUI()"); + serialPort0.println("Config::startUI()"); #endif enableUI(); lcd->start(); @@ -69,7 +69,7 @@ void UI::startUI() { void UI::disableUI() { #if DEBUG == 2 - Serial.println("Config::disableUI()"); + serialPort0.println("Config::disableUI()"); #endif config->disableUI(); lcd->turnOff(); @@ -79,7 +79,7 @@ void UI::disableUI() { void UI::enableUI() { #if DEBUG == 2 - Serial.println("Config::enableUI()"); + serialPort0.println("Config::enableUI()"); #endif config->enableUI(); lcd->turnOn(); @@ -103,8 +103,8 @@ void UI::processInputEvents() { } #if DEBUG == 2 - Serial.print("Joystick Event: "); - Serial.println(joyStickEvent); + serialPort0.print("Joystick Event: "); + serialPort0.println(joyStickEvent); #endif resetTimeout(); lcd->handleJoystickEvent(joyStickEvent); diff --git a/Universal_Serial_Adapter/UIButton.cpp b/Universal_Serial_Adapter/UIButton.cpp index 38fcead..b8dd378 100644 --- a/Universal_Serial_Adapter/UIButton.cpp +++ b/Universal_Serial_Adapter/UIButton.cpp @@ -14,7 +14,7 @@ UIButton::UIButton(int buttonPin, int ledPin) { #if DEBUG == 2 - Serial.println("UIButton::UIButton()"); + serialPort0.println("UIButton::UIButton()"); #endif this->buttonPin = buttonPin; this->ledPin = ledPin; @@ -23,7 +23,7 @@ UIButton::UIButton(int buttonPin, int ledPin) { void UIButton::setup() { #if DEBUG == 2 - Serial.println("UIButton::setup()"); + serialPort0.println("UIButton::setup()"); #endif pinMode(buttonPin, INPUT); pinMode(ledPin, OUTPUT); @@ -31,25 +31,25 @@ void UIButton::setup() { void UIButton::turnOnLed() { #if DEBUG == 2 - Serial.println("UIButton::turnOnLed()"); - Serial.print("Turning on pin: "); - Serial.println(ledPin); + serialPort0.println("UIButton::turnOnLed()"); + serialPort0.print("Turning on pin: "); + serialPort0.println(ledPin); #endif digitalWrite(ledPin, HIGH); } void UIButton::turnOffLed() { #if DEBUG == 2 - Serial.println("UIButton::turnOffLed()"); - Serial.print("Turning off pin: "); - Serial.println(ledPin); + serialPort0.println("UIButton::turnOffLed()"); + serialPort0.print("Turning off pin: "); + serialPort0.println(ledPin); #endif digitalWrite(ledPin, LOW); } bool UIButton::isPressed() { // if (DEBUG) { - //Serial.println("UIButton::isPressed()"); + //serialPort0.println("UIButton::isPressed()"); // } bool pressed = digitalRead(buttonPin); while (digitalRead(buttonPin)); // Wait for release diff --git a/Universal_Serial_Adapter/UIJoystickPSP.cpp b/Universal_Serial_Adapter/UIJoystickPSP.cpp index 446369f..3912687 100644 --- a/Universal_Serial_Adapter/UIJoystickPSP.cpp +++ b/Universal_Serial_Adapter/UIJoystickPSP.cpp @@ -8,13 +8,12 @@ Serial Adapter Project: Dynamic serial TTY passthroughs attribute. */ -#include "Arduino.h" #include "Project.h" #include "UIJoystickPSP.h" UIJoystickPSP::UIJoystickPSP(int xAxisPin, int yAxisPin) { #if DEBUG == 2 - Serial.println("UIJoystickPSP::UIJoystickPSP()"); + serialPort0.println("UIJoystickPSP::UIJoystickPSP()"); #endif this->xAxisPin = xAxisPin; this->yAxisPin = yAxisPin; @@ -29,7 +28,7 @@ UIJoystickPSP::UIJoystickPSP(int xAxisPin, int yAxisPin) { joyDirection UIJoystickPSP::direction() { // if (DEBUG) { - //Serial.println("UIJoystickPSP::direction()"); + //serialPort0.println("UIJoystickPSP::direction()"); // } // Read the x/y values from the joystick xAxisValue = map(analogRead(xAxisPin), 0, 1023, 0, 10); diff --git a/Universal_Serial_Adapter/UILCD.cpp b/Universal_Serial_Adapter/UILCD.cpp index 0793535..9b83654 100644 --- a/Universal_Serial_Adapter/UILCD.cpp +++ b/Universal_Serial_Adapter/UILCD.cpp @@ -8,11 +8,9 @@ attribute. */ -#include "Arduino.h" - #include #include -#include +#include #include #include "Project.h" @@ -21,22 +19,18 @@ UILCD::UILCD() { #if DEBUG == 2 - Serial.println("UILCD::UILCD()"); + serialPort0.println("UILCD::UILCD()"); #endif pinMode(LCD_LITE, OUTPUT); tft = new Adafruit_ST7735(LCD_CS, LCD_DC, LCD_RST); tft->initR(INITR_BLACKTAB); tft->setRotation(3); - - if (!SD.begin(SD_CS)) { - Serial.println("SD.begin(SD_CS) -- failed!"); - } } void UILCD::start() { #if DEBUG == 2 - Serial.println("UILCD::start()"); + serialPort0.println("UILCD::start()"); #endif drawSplashScreen(); drawMainScreen(); @@ -44,24 +38,24 @@ void UILCD::start() { void UILCD::turnOn() { #if DEBUG == 2 - Serial.println("UILCD::turnOn()"); + serialPort0.println("UILCD::turnOn()"); #endif digitalWrite(LCD_LITE, HIGH); } void UILCD::turnOff() { #if DEBUG == 2 - Serial.println("UILCD::turnOff()"); + serialPort0.println("UILCD::turnOff()"); #endif digitalWrite(LCD_LITE, LOW); } void UILCD::handleJoystickEvent(joyDirection direction) { #if DEBUG == 2 - Serial.println("UILCD::handleJoystickEvent()"); - Serial.println("begin UILCD::handleJoystickEvent"); - Serial.print("Current Screen: "); - Serial.println(currentScreen); + serialPort0.println("UILCD::handleJoystickEvent()"); + serialPort0.println("begin UILCD::handleJoystickEvent"); + serialPort0.print("Current Screen: "); + serialPort0.println(currentScreen); #endif switch (currentScreen) { @@ -76,10 +70,10 @@ void UILCD::handleJoystickEvent(joyDirection direction) { void UILCD::handleOkButtonEvent() { #if DEBUG == 2 - Serial.println("UILCD::handleOkButtonEvent()"); - Serial.println("begin UILCD::handleOkButtonEvent"); - Serial.print("Current Screen: "); - Serial.println(currentScreen); + serialPort0.println("UILCD::handleOkButtonEvent()"); + serialPort0.println("begin UILCD::handleOkButtonEvent"); + serialPort0.print("Current Screen: "); + serialPort0.println(currentScreen); #endif switch (currentScreen) { @@ -107,10 +101,10 @@ void UILCD::handleOkButtonEvent() { void UILCD::handleCancelButtonEvent() { #if DEBUG == 2 - Serial.println("UILCD::handleCancelButtonEvent"); - Serial.println("begin UILCD::handleCancelButtonEvent"); - Serial.print("Current Screen: "); - Serial.println(currentScreen); + serialPort0.println("UILCD::handleCancelButtonEvent"); + serialPort0.println("begin UILCD::handleCancelButtonEvent"); + serialPort0.print("Current Screen: "); + serialPort0.println(currentScreen); #endif switch (currentScreen) { @@ -125,7 +119,7 @@ void UILCD::handleCancelButtonEvent() { void UILCD::mainScreenOkButton() { #if DEBUG == 2 - Serial.println("UILCD::mainScreenOkButton"); + serialPort0.println("UILCD::mainScreenOkButton"); #endif switch(currentLine) { case 0: // Connection Type @@ -151,14 +145,14 @@ void UILCD::mainScreenOkButton() { void UILCD::mainScreenCancelButton() { #if DEBUG == 2 - Serial.println("UILCD::mainScreenCancelButton()"); + serialPort0.println("UILCD::mainScreenCancelButton()"); #endif // Do nothing for now } void UILCD::unHilightLine(int line) { #if DEBUG == 2 - Serial.println("UILCD::unHilightLine()"); + serialPort0.println("UILCD::unHilightLine()"); #endif tft->setCursor(0, line * FONT_HEIGHT); tft->fillRect(0, line * FONT_HEIGHT, FONT_WIDTH, FONT_HEIGHT, BACKGROUND); @@ -166,7 +160,7 @@ void UILCD::unHilightLine(int line) { void UILCD::hilightLine(int line) { #if DEBUG == 2 - Serial.println("UILCD::hilightLine()"); + serialPort0.println("UILCD::hilightLine()"); #endif tft->setCursor(0, line * FONT_HEIGHT); tft->setTextColor(HILIGHT); @@ -175,7 +169,7 @@ void UILCD::hilightLine(int line) { void UILCD::mainScreenHilight(joyDirection direction) { #if DEBUG == 2 - Serial.println("UILCD::mainScreenHilight()"); + serialPort0.println("UILCD::mainScreenHilight()"); #endif if (direction == joyUp) { // Don't go up past the 1st line @@ -189,7 +183,7 @@ void UILCD::mainScreenHilight(joyDirection direction) { // Skip blank lines if (config->getSerialMode() == ttl) { #if DEBUG == 2 - Serial.println("Serial ttl blank line skip"); + serialPort0.println("Serial ttl blank line skip"); #endif if (currentLine == 3 || currentLine == 6) { currentLine -= 1; @@ -197,7 +191,7 @@ void UILCD::mainScreenHilight(joyDirection direction) { } else { #if DEBUG == 2 - Serial.println("Non-serial ttl blank line skip"); + serialPort0.println("Non-serial ttl blank line skip"); #endif if (currentLine == 2 || currentLine == 5) { currentLine -= 1; @@ -226,7 +220,7 @@ void UILCD::mainScreenHilight(joyDirection direction) { // Skip blank lines if (config->getSerialMode() == ttl) { #if DEBUG == 2 - Serial.println("Serial ttl blank line skip"); + serialPort0.println("Serial ttl blank line skip"); #endif if (currentLine == 3 || currentLine == 6) { currentLine += 1; @@ -234,7 +228,7 @@ void UILCD::mainScreenHilight(joyDirection direction) { } else { #if DEBUG == 2 - Serial.println("Non-serial ttl blank line skip"); + serialPort0.println("Non-serial ttl blank line skip"); #endif if (currentLine == 2 || currentLine == 5) { currentLine += 1; @@ -247,7 +241,7 @@ void UILCD::mainScreenHilight(joyDirection direction) { void UILCD::configScreenHighlight(joyDirection direction) { #if DEBUG == 2 - Serial.println("UILCD::configScreenhilight()"); + serialPort0.println("UILCD::configScreenhilight()"); #endif if (direction == joyUp) { // Don't go up past the 1st line @@ -296,7 +290,7 @@ void UILCD::configScreenHighlight(joyDirection direction) { void UILCD::drawTimeoutScreen(bool keepCurrentLine) { #if DEBUG == 2 - Serial.println("UILCD::drawTimeoutScreen()"); + serialPort0.println("UILCD::drawTimeoutScreen()"); #endif currentScreen = timeoutscreen; if (!keepCurrentLine) { @@ -331,7 +325,7 @@ void UILCD::drawTimeoutScreen(bool keepCurrentLine) { void UILCD::drawConnectionScreen(bool keepCurrentLine) { #if DEBUG == 2 - Serial.println("UILCD::drawConnectionScreen()"); + serialPort0.println("UILCD::drawConnectionScreen()"); #endif currentScreen = connectionScreen; if (!keepCurrentLine) { @@ -366,7 +360,7 @@ void UILCD::drawConnectionScreen(bool keepCurrentLine) { void UILCD::drawLineSpeedScreen(bool keepCurrentLine) { #if DEBUG == 2 - Serial.println("UILCD::drawLineSpeedScreen()"); + serialPort0.println("UILCD::drawLineSpeedScreen()"); #endif currentScreen = lineSpeedScreen; if (!keepCurrentLine) { @@ -401,7 +395,7 @@ void UILCD::drawLineSpeedScreen(bool keepCurrentLine) { void UILCD::drawVoltageScreen(bool keepCurrentLine) { #if DEBUG == 2 - Serial.println("UILCD::drawVoltageScreen()"); + serialPort0.println("UILCD::drawVoltageScreen()"); #endif currentScreen = voltageScreen; if (!keepCurrentLine) { @@ -436,7 +430,7 @@ void UILCD::drawVoltageScreen(bool keepCurrentLine) { void UILCD::drawMainScreen() { #if DEBUG == 2 - Serial.println("UILCD::drawMainScreen()"); + serialPort0.println("UILCD::drawMainScreen()"); #endif currentScreen = mainScreen; currentLine = 0; @@ -468,7 +462,7 @@ void UILCD::drawMainScreen() { void UILCD::drawSplashScreen() { #if DEBUG == 2 - Serial.println("UILCD::drawSplashScreen()"); + serialPort0.println("UILCD::drawSplashScreen()"); #endif tft->setCursor(0,0); tft->fillScreen(SPLASH_BACKGROUND); @@ -482,10 +476,10 @@ void UILCD::drawSplashScreen() { void UILCD::bmpDraw(char *filename, uint8_t x, uint8_t y) { #if DEBUG == 2 - Serial.println("UILCD::bmpDraw()"); + serialPort0.println("UILCD::bmpDraw()"); #endif - File bmpFile; + SdFile bmpFile; int bmpWidth, bmpHeight; // W+H in pixels uint8_t bmpDepth; // Bit depth (currently must be 24) uint32_t bmpImageoffset; // Start of image data in file @@ -503,15 +497,15 @@ void UILCD::bmpDraw(char *filename, uint8_t x, uint8_t y) { } #if DEBUG == 2 - Serial.println(); - Serial.print("Loading image '"); - Serial.print(filename); - Serial.println('\''); + serialPort0.println(); + serialPort0.print("Loading image '"); + serialPort0.print(filename); + serialPort0.println('\''); #endif // Open requested file on SD card - if ((bmpFile = SD.open(filename)) == NULL) { - Serial.print("File not found"); + if (!bmpFile.open(filename)) { + serialPort0.print("File not found"); return; } @@ -519,24 +513,24 @@ void UILCD::bmpDraw(char *filename, uint8_t x, uint8_t y) { if(read16(bmpFile) == 0x4D42) { // BMP signature long fileSize = read32(bmpFile); #if DEBUG == 2 - Serial.print("File size: "); - Serial.println(fileSize); + serialPort0.print("File size: "); + serialPort0.println(fileSize); #endif (void)read32(bmpFile); // Read & ignore creator bytes bmpImageoffset = read32(bmpFile); // Start of image data #if DEBUG == 2 - Serial.print("Image Offset: "); - Serial.println(bmpImageoffset, DEC); + serialPort0.print("Image Offset: "); + serialPort0.println(bmpImageoffset, DEC); #endif float headerSize = read32(bmpFile); #if DEBUG == 2 // Read DIB header - Serial.print("Header size: "); - Serial.println(headerSize); + serialPort0.print("Header size: "); + serialPort0.println(headerSize); #endif bmpWidth = read32(bmpFile); @@ -544,16 +538,16 @@ void UILCD::bmpDraw(char *filename, uint8_t x, uint8_t y) { if(read16(bmpFile) == 1) { // # planes -- must be '1' bmpDepth = read16(bmpFile); // bits per pixel #if DEBUG == 2 - Serial.print("Bit Depth: "); - Serial.println(bmpDepth); + serialPort0.print("Bit Depth: "); + serialPort0.println(bmpDepth); #endif if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed goodBmp = true; // Supported BMP format -- proceed! #if DEBUG == 2 - Serial.print("Image size: "); - Serial.print(bmpWidth); - Serial.print('x'); - Serial.println(bmpHeight); + serialPort0.print("Image size: "); + serialPort0.print(bmpWidth); + serialPort0.print('x'); + serialPort0.println(bmpHeight); #endif // BMP rows are padded (if needed) to 4-byte boundary @@ -593,8 +587,8 @@ void UILCD::bmpDraw(char *filename, uint8_t x, uint8_t y) { else { // Bitmap is stored top-to-bottom pos = bmpImageoffset + row * rowSize; } - if(bmpFile.position() != pos) { // Need seek? - bmpFile.seek(pos); + if(bmpFile.curPosition() != pos) { // Need seek? + bmpFile.seekCur(pos); buffidx = sizeof(sdbuffer); // Force buffer reload } @@ -614,9 +608,9 @@ void UILCD::bmpDraw(char *filename, uint8_t x, uint8_t y) { delay(5); } // end scanline #if DEBUG == 2 - Serial.print("Loaded in "); - Serial.print(millis() - startTime); - Serial.println(" ms"); + serialPort0.print("Loaded in "); + serialPort0.print(millis() - startTime); + serialPort0.println(" ms"); #endif } // end goodBmp } @@ -624,7 +618,7 @@ void UILCD::bmpDraw(char *filename, uint8_t x, uint8_t y) { bmpFile.close(); if(!goodBmp) { - Serial.println("BMP format not recognized."); + serialPort0.println("BMP format not recognized."); } } @@ -632,9 +626,9 @@ void UILCD::bmpDraw(char *filename, uint8_t x, uint8_t y) { // BMP data is stored little-endian, Arduino is little-endian too. // May need to reverse subscript order if porting elsewhere. -uint16_t UILCD::read16(File f) { +uint16_t UILCD::read16(SdFile f) { #if DEBUG == 2 - Serial.println("UILCD::read16()"); + serialPort0.println("UILCD::read16()"); #endif uint16_t result; ((uint8_t *)&result)[0] = f.read(); // LSB @@ -642,9 +636,9 @@ uint16_t UILCD::read16(File f) { return result; } -uint32_t UILCD::read32(File f) { +uint32_t UILCD::read32(SdFile f) { #if DEBUG == 2 - Serial.println("UILCD::read32()"); + serialPort0.println("UILCD::read32()"); #endif uint32_t result; ((uint8_t *)&result)[0] = f.read(); // LSB diff --git a/Universal_Serial_Adapter/UILCD.h b/Universal_Serial_Adapter/UILCD.h index fdc535d..29f29f4 100644 --- a/Universal_Serial_Adapter/UILCD.h +++ b/Universal_Serial_Adapter/UILCD.h @@ -11,11 +11,9 @@ #ifndef UILCD_h #define UILCD_h -#include "Arduino.h" - #include #include -#include +#include #include #include "Project.h" @@ -46,8 +44,8 @@ private: int currentLine; screen currentScreen; - uint32_t read32(File f); - uint16_t read16(File f); + uint32_t read32(SdFile f); + uint16_t read16(SdFile f); void drawSplashScreen(); void drawMainScreen(); diff --git a/Universal_Serial_Adapter/Universal_Serial_Adapter.ino b/Universal_Serial_Adapter/Universal_Serial_Adapter.ino index fc68ec9..5305679 100644 --- a/Universal_Serial_Adapter/Universal_Serial_Adapter.ino +++ b/Universal_Serial_Adapter/Universal_Serial_Adapter.ino @@ -19,8 +19,10 @@ #include #include -#include #include +#include +#include +#include #include #include @@ -29,7 +31,12 @@ UI* ui; Config* config; RTC_DS1307 rtc; -File dataFile; +SdFat sd; +SdFile dataFile; +SerialPort<0, 4096, 0> serialPort0; +SerialPort<0, 4096, 0> serialPort1; +SerialPort<0, 4096, 0> serialPort2; +SerialPort<0, 4096, 0> serialPort3; // helper for interrupt method call void processSerial() { @@ -38,7 +45,7 @@ void processSerial() { void setup() { // Setup various IO busses - Serial.begin(115200); + serialPort0.begin(115200); Wire.begin(); // Ensure RTC is set to run on battery @@ -52,6 +59,7 @@ void setup() { Wire.write(0b00011100); // write register bitmap, bit 7 is /EOSC Wire.endTransmission(); + // Start real time clock rtc.begin(); // Stuff used for serial and UI @@ -59,13 +67,20 @@ void setup() { config->setDefaults(); ui = new UI(); + // Start the SD Card -- FIXME: This should likely get moved to config + if (!sd.begin(SD_CS)) { + serialPort0.println("SD.begin(SD_CS) -- failed!"); + } + + // Open data log file -- FIXME: This needs to be controlled via config some + dataFile.open("datalog.txt", O_WRITE | O_CREAT | O_AT_END); + // Setup serial IO on an interrupt timer // ***THIS MUST BE DONE AFTER new Config()*** + // ***THIS MUST BE DONE AFTER SD INIT*** Timer3.initialize(100); // initialize timer3, value in micro seconds Timer3.pwm(timerThreePin, 512); // setup pwm on appropriate pin, 50% duty cycle Timer3.attachInterrupt(processSerial); // attaches method as a timer overflow interrupt - - dataFile = SD.open("datalog.txt", FILE_WRITE); } void loop() {