updated to sdfatlib2010902
This commit is contained in:
parent
46f80e82d9
commit
64f2121ab1
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
|
|
||||||
// This determines the communication speed of the printer
|
// This determines the communication speed of the printer
|
||||||
#define BAUDRATE 250000
|
//#define BAUDRATE 250000
|
||||||
//#define BAUDRATE 115200
|
#define BAUDRATE 115200
|
||||||
//#define BAUDRATE 230400
|
//#define BAUDRATE 230400
|
||||||
|
|
||||||
|
|
||||||
|
@ -277,7 +277,7 @@ const bool ENDSTOPS_INVERTING = true; // set to true to invert the logic of the
|
||||||
|
|
||||||
#define ULTIPANEL
|
#define ULTIPANEL
|
||||||
#ifdef ULTIPANEL
|
#ifdef ULTIPANEL
|
||||||
// #define NEWPANEL //enable this if you have a click-encoder panel
|
#define NEWPANEL //enable this if you have a click-encoder panel
|
||||||
#define SDSUPPORT
|
#define SDSUPPORT
|
||||||
#define ULTRA_LCD
|
#define ULTRA_LCD
|
||||||
#define LCD_WIDTH 20
|
#define LCD_WIDTH 20
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
|
|
||||||
#include "EEPROMwrite.h"
|
#include "EEPROMwrite.h"
|
||||||
#include "fastio.h"
|
#include "fastio.h"
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
|
@ -391,9 +392,15 @@ inline void get_command()
|
||||||
while( !card.eof() && buflen < BUFSIZE) {
|
while( !card.eof() && buflen < BUFSIZE) {
|
||||||
int16_t n=card.get();
|
int16_t n=card.get();
|
||||||
serial_char = (char)n;
|
serial_char = (char)n;
|
||||||
|
// Serial.print((char)serial_char);
|
||||||
|
// Serial.print(" ");
|
||||||
|
// Serial.println((int)serial_count);
|
||||||
if(serial_char == '\n' || serial_char == '\r' || serial_char == ':' || serial_count >= (MAX_CMD_SIZE - 1)||n==-1)
|
if(serial_char == '\n' || serial_char == '\r' || serial_char == ':' || serial_count >= (MAX_CMD_SIZE - 1)||n==-1)
|
||||||
{
|
{
|
||||||
|
// if(serial_char == '\n' || serial_char == '\r' )
|
||||||
|
// Serial.println("newline or :");
|
||||||
|
// if(serial_count >= (MAX_CMD_SIZE - 1))
|
||||||
|
// Serial.println("too long line");
|
||||||
if(card.eof()){
|
if(card.eof()){
|
||||||
card.sdprinting = false;
|
card.sdprinting = false;
|
||||||
SERIAL_PROTOCOLLNPGM("Done printing file");
|
SERIAL_PROTOCOLLNPGM("Done printing file");
|
||||||
|
@ -409,15 +416,20 @@ inline void get_command()
|
||||||
LCD_MESSAGE(time);
|
LCD_MESSAGE(time);
|
||||||
card.checkautostart(true);
|
card.checkautostart(true);
|
||||||
}
|
}
|
||||||
|
if(serial_char=='\n')
|
||||||
|
comment_mode = false; //for new command
|
||||||
if(!serial_count)
|
if(!serial_count)
|
||||||
return; //if empty line
|
{
|
||||||
|
// Serial.println("empty line");
|
||||||
|
return; //if empty line
|
||||||
|
}
|
||||||
cmdbuffer[bufindw][serial_count] = 0; //terminate string
|
cmdbuffer[bufindw][serial_count] = 0; //terminate string
|
||||||
if(!comment_mode){
|
if(!comment_mode){
|
||||||
fromsd[bufindw] = true;
|
fromsd[bufindw] = true;
|
||||||
buflen += 1;
|
buflen += 1;
|
||||||
bufindw = (bufindw + 1)%BUFSIZE;
|
bufindw = (bufindw + 1)%BUFSIZE;
|
||||||
}
|
}
|
||||||
comment_mode = false; //for new command
|
|
||||||
serial_count = 0; //clear buffer
|
serial_count = 0; //clear buffer
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -17,28 +17,77 @@
|
||||||
* along with the Arduino Sd2Card Library. If not, see
|
* along with the Arduino Sd2Card Library. If not, see
|
||||||
* <http://www.gnu.org/licenses/>.
|
* <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#if ARDUINO < 100
|
||||||
#include <WProgram.h>
|
#include <WProgram.h>
|
||||||
|
#else // ARDUINO
|
||||||
|
#include <Arduino.h>
|
||||||
|
#endif // ARDUINO
|
||||||
#include "Sd2Card.h"
|
#include "Sd2Card.h"
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#ifndef SOFTWARE_SPI
|
#ifndef SOFTWARE_SPI
|
||||||
// functions for hardware SPI
|
// functions for hardware SPI
|
||||||
/** Send a byte to the card */
|
//------------------------------------------------------------------------------
|
||||||
|
// make sure SPCR rate is in expected bits
|
||||||
|
#if (SPR0 != 0 || SPR1 != 1)
|
||||||
|
#error unexpected SPCR bits
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* Initialize hardware SPI
|
||||||
|
* Set SCK rate to F_CPU/pow(2, 1 + spiRate) for spiRate [0,6]
|
||||||
|
*/
|
||||||
|
static void spiInit(uint8_t spiRate) {
|
||||||
|
// 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 read data - only one call so force inline */
|
||||||
|
static inline __attribute__((always_inline))
|
||||||
|
void spiRead(uint8_t* buf, uint16_t nbyte) {
|
||||||
|
if (nbyte-- == 0) return;
|
||||||
|
SPDR = 0XFF;
|
||||||
|
for (uint16_t i = 0; i < nbyte; i++) {
|
||||||
|
while (!(SPSR & (1 << SPIF)));
|
||||||
|
buf[i] = SPDR;
|
||||||
|
SPDR = 0XFF;
|
||||||
|
}
|
||||||
|
while (!(SPSR & (1 << SPIF)));
|
||||||
|
buf[nbyte] = SPDR;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** SPI send a byte */
|
||||||
static void spiSend(uint8_t b) {
|
static void spiSend(uint8_t b) {
|
||||||
SPDR = b;
|
SPDR = b;
|
||||||
while (!(SPSR & (1 << SPIF)));
|
while (!(SPSR & (1 << SPIF)));
|
||||||
}
|
}
|
||||||
/** Receive a byte from the card */
|
//------------------------------------------------------------------------------
|
||||||
static uint8_t spiRec(void) {
|
/** SPI send block - only one call so force inline */
|
||||||
spiSend(0XFF);
|
static inline __attribute__((always_inline))
|
||||||
return SPDR;
|
void spiSendBlock(uint8_t token, const uint8_t* buf) {
|
||||||
|
SPDR = token;
|
||||||
|
for (uint16_t i = 0; i < 512; i += 2) {
|
||||||
|
while (!(SPSR & (1 << SPIF)));
|
||||||
|
SPDR = buf[i];
|
||||||
|
while (!(SPSR & (1 << SPIF)));
|
||||||
|
SPDR = buf[i + 1];
|
||||||
|
}
|
||||||
|
while (!(SPSR & (1 << SPIF)));
|
||||||
}
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
#else // SOFTWARE_SPI
|
#else // SOFTWARE_SPI
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/** nop to tune soft SPI timing */
|
/** nop to tune soft SPI timing */
|
||||||
#define nop asm volatile ("nop\n\t")
|
#define nop asm volatile ("nop\n\t")
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/** Soft SPI receive */
|
/** Soft SPI receive byte */
|
||||||
uint8_t spiRec(void) {
|
static uint8_t spiRec() {
|
||||||
uint8_t data = 0;
|
uint8_t data = 0;
|
||||||
// no interrupts during byte receive - about 8 us
|
// no interrupts during byte receive - about 8 us
|
||||||
cli();
|
cli();
|
||||||
|
@ -63,8 +112,15 @@ uint8_t spiRec(void) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/** Soft SPI send */
|
/** Soft SPI read data */
|
||||||
void spiSend(uint8_t data) {
|
static void spiRead(uint8_t* buf, uint16_t nbyte) {
|
||||||
|
for (uint16_t i = 0; i < nbyte; i++) {
|
||||||
|
buf[i] = spiRec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** Soft SPI send byte */
|
||||||
|
static void spiSend(uint8_t data) {
|
||||||
// no interrupts during byte send - about 8 us
|
// no interrupts during byte send - about 8 us
|
||||||
cli();
|
cli();
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
@ -86,13 +142,18 @@ void spiSend(uint8_t data) {
|
||||||
// enable interrupts
|
// enable interrupts
|
||||||
sei();
|
sei();
|
||||||
}
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** Soft SPI send block */
|
||||||
|
void spiSendBlock(uint8_t token, const uint8_t* buf) {
|
||||||
|
spiSend(token);
|
||||||
|
for (uint16_t i = 0; i < 512; i++) {
|
||||||
|
spiSend(buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // SOFTWARE_SPI
|
#endif // SOFTWARE_SPI
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// send command and return error code. Return zero for OK
|
// send command and return error code. Return zero for OK
|
||||||
uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
|
uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
|
||||||
// end read if in partialBlockRead mode
|
|
||||||
readEnd();
|
|
||||||
|
|
||||||
// select card
|
// select card
|
||||||
chipSelectLow();
|
chipSelectLow();
|
||||||
|
|
||||||
|
@ -111,6 +172,9 @@ uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
|
||||||
if (cmd == CMD8) crc = 0X87; // correct crc for CMD8 with arg 0X1AA
|
if (cmd == CMD8) crc = 0X87; // correct crc for CMD8 with arg 0X1AA
|
||||||
spiSend(crc);
|
spiSend(crc);
|
||||||
|
|
||||||
|
// skip stuff byte for stop read
|
||||||
|
if (cmd == CMD12) spiRec();
|
||||||
|
|
||||||
// wait for response
|
// wait for response
|
||||||
for (uint8_t i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++);
|
for (uint8_t i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++);
|
||||||
return status_;
|
return status_;
|
||||||
|
@ -122,7 +186,7 @@ uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
|
||||||
* \return The number of 512 byte data blocks in the card
|
* \return The number of 512 byte data blocks in the card
|
||||||
* or zero if an error occurs.
|
* or zero if an error occurs.
|
||||||
*/
|
*/
|
||||||
uint32_t Sd2Card::cardSize(void) {
|
uint32_t Sd2Card::cardSize() {
|
||||||
csd_t csd;
|
csd_t csd;
|
||||||
if (!readCSD(&csd)) return 0;
|
if (!readCSD(&csd)) return 0;
|
||||||
if (csd.v1.csd_ver == 0) {
|
if (csd.v1.csd_ver == 0) {
|
||||||
|
@ -142,11 +206,14 @@ uint32_t Sd2Card::cardSize(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void Sd2Card::chipSelectHigh(void) {
|
void Sd2Card::chipSelectHigh() {
|
||||||
digitalWrite(chipSelectPin_, HIGH);
|
digitalWrite(chipSelectPin_, HIGH);
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void Sd2Card::chipSelectLow(void) {
|
void Sd2Card::chipSelectLow() {
|
||||||
|
#ifndef SOFTWARE_SPI
|
||||||
|
spiInit(spiRate_);
|
||||||
|
#endif // SOFTWARE_SPI
|
||||||
digitalWrite(chipSelectPin_, LOW);
|
digitalWrite(chipSelectPin_, LOW);
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
@ -163,10 +230,18 @@ void Sd2Card::chipSelectLow(void) {
|
||||||
* \return The value one, true, is returned for success and
|
* \return The value one, true, is returned for success and
|
||||||
* the value zero, false, is returned for failure.
|
* the value zero, false, is returned for failure.
|
||||||
*/
|
*/
|
||||||
uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
|
bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
|
||||||
if (!eraseSingleBlockEnable()) {
|
csd_t csd;
|
||||||
error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK);
|
if (!readCSD(&csd)) goto fail;
|
||||||
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) {
|
if (type_ != SD_CARD_TYPE_SDHC) {
|
||||||
firstBlock <<= 9;
|
firstBlock <<= 9;
|
||||||
|
@ -195,9 +270,9 @@ uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
|
||||||
* \return The value one, true, is returned if single block erase is supported.
|
* \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.
|
* The value zero, false, is returned if single block erase is not supported.
|
||||||
*/
|
*/
|
||||||
uint8_t Sd2Card::eraseSingleBlockEnable(void) {
|
bool Sd2Card::eraseSingleBlockEnable() {
|
||||||
csd_t csd;
|
csd_t csd;
|
||||||
return readCSD(&csd) ? csd.v1.erase_blk_en : 0;
|
return readCSD(&csd) ? csd.v1.erase_blk_en : false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
|
@ -210,8 +285,8 @@ uint8_t Sd2Card::eraseSingleBlockEnable(void) {
|
||||||
* the value zero, false, is returned for failure. The reason for failure
|
* the value zero, false, is returned for failure. The reason for failure
|
||||||
* can be determined by calling errorCode() and errorData().
|
* can be determined by calling errorCode() and errorData().
|
||||||
*/
|
*/
|
||||||
uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
|
bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
|
||||||
errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0;
|
errorCode_ = type_ = 0;
|
||||||
chipSelectPin_ = chipSelectPin;
|
chipSelectPin_ = chipSelectPin;
|
||||||
// 16-bit init start time allows over a minute
|
// 16-bit init start time allows over a minute
|
||||||
uint16_t t0 = (uint16_t)millis();
|
uint16_t t0 = (uint16_t)millis();
|
||||||
|
@ -227,17 +302,18 @@ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
|
||||||
#ifndef SOFTWARE_SPI
|
#ifndef SOFTWARE_SPI
|
||||||
// SS must be in output mode even it is not chip select
|
// SS must be in output mode even it is not chip select
|
||||||
pinMode(SS_PIN, OUTPUT);
|
pinMode(SS_PIN, OUTPUT);
|
||||||
// Enable SPI, Master, clock rate f_osc/128
|
// set SS high - may be chip select for another SPI device
|
||||||
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
|
#if SET_SPI_SS_HIGH
|
||||||
// clear double speed
|
digitalWrite(SS_PIN, HIGH);
|
||||||
SPSR &= ~(1 << SPI2X);
|
#endif // SET_SPI_SS_HIGH
|
||||||
|
// set SCK rate for initialization commands
|
||||||
|
spiRate_ = SPI_SD_INIT_RATE;
|
||||||
|
spiInit(spiRate_);
|
||||||
#endif // SOFTWARE_SPI
|
#endif // SOFTWARE_SPI
|
||||||
|
|
||||||
// must supply min of 74 clock cycles with CS high.
|
// must supply min of 74 clock cycles with CS high.
|
||||||
for (uint8_t i = 0; i < 10; i++) spiSend(0XFF);
|
for (uint8_t i = 0; i < 10; i++) spiSend(0XFF);
|
||||||
|
|
||||||
chipSelectLow();
|
|
||||||
|
|
||||||
// command to go idle in SPI mode
|
// command to go idle in SPI mode
|
||||||
while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
|
while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
|
||||||
if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
|
if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
|
||||||
|
@ -291,105 +367,60 @@ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Enable or disable partial block reads.
|
* Read a 512 byte block from an SD card.
|
||||||
*
|
*
|
||||||
* Enabling partial block reads improves performance by allowing a block
|
* \param[in] blockNumber Logical block to be read.
|
||||||
* to be read over the SPI bus as several sub-blocks. Errors may occur
|
|
||||||
* if the time between reads is too long since the SD card may timeout.
|
|
||||||
* The SPI SS line will be held low until the entire block is read or
|
|
||||||
* readEnd() is called.
|
|
||||||
*
|
|
||||||
* Use this for applications like the Adafruit Wave Shield.
|
|
||||||
*
|
|
||||||
* \param[in] value The value TRUE (non-zero) or FALSE (zero).)
|
|
||||||
*/
|
|
||||||
void Sd2Card::partialBlockRead(uint8_t value) {
|
|
||||||
readEnd();
|
|
||||||
partialBlockRead_ = value;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* Read a 512 byte block from an SD card device.
|
|
||||||
*
|
|
||||||
* \param[in] block Logical block to be read.
|
|
||||||
* \param[out] dst Pointer to the location that will receive the data.
|
* \param[out] dst Pointer to the location that will receive the data.
|
||||||
|
|
||||||
* \return The value one, true, is returned for success and
|
* \return The value one, true, is returned for success and
|
||||||
* the value zero, false, is returned for failure.
|
* the value zero, false, is returned for failure.
|
||||||
*/
|
*/
|
||||||
uint8_t Sd2Card::readBlock(uint32_t block, uint8_t* dst) {
|
bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
|
||||||
return readData(block, 0, 512, dst);
|
// use address if not SDHC card
|
||||||
}
|
if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9;
|
||||||
//------------------------------------------------------------------------------
|
if (cardCommand(CMD17, blockNumber)) {
|
||||||
/**
|
error(SD_CARD_ERROR_CMD17);
|
||||||
* Read part of a 512 byte block from an SD card.
|
|
||||||
*
|
|
||||||
* \param[in] block Logical block to be read.
|
|
||||||
* \param[in] offset Number of bytes to skip at start of block
|
|
||||||
* \param[out] dst Pointer to the location that will receive the data.
|
|
||||||
* \param[in] count Number of bytes to read
|
|
||||||
* \return The value one, true, is returned for success and
|
|
||||||
* the value zero, false, is returned for failure.
|
|
||||||
*/
|
|
||||||
uint8_t Sd2Card::readData(uint32_t block,
|
|
||||||
uint16_t offset, uint16_t count, uint8_t* dst) {
|
|
||||||
uint16_t n;
|
|
||||||
if (count == 0) return true;
|
|
||||||
if ((count + offset) > 512) {
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (!inBlock_ || block != block_ || offset < offset_) {
|
return readData(dst, 512);
|
||||||
block_ = block;
|
|
||||||
// use address if not SDHC card
|
fail:
|
||||||
if (type()!= SD_CARD_TYPE_SDHC) block <<= 9;
|
chipSelectHigh();
|
||||||
if (cardCommand(CMD17, block)) {
|
return false;
|
||||||
error(SD_CARD_ERROR_CMD17);
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** 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, uint16_t count) {
|
||||||
|
// 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;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (!waitStartBlock()) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
offset_ = 0;
|
|
||||||
inBlock_ = 1;
|
|
||||||
}
|
}
|
||||||
|
if (status_ != DATA_START_BLOCK) {
|
||||||
#ifdef OPTIMIZE_HARDWARE_SPI
|
error(SD_CARD_ERROR_READ);
|
||||||
// start first spi transfer
|
goto fail;
|
||||||
SPDR = 0XFF;
|
|
||||||
|
|
||||||
// skip data before offset
|
|
||||||
for (;offset_ < offset; offset_++) {
|
|
||||||
while (!(SPSR & (1 << SPIF)));
|
|
||||||
SPDR = 0XFF;
|
|
||||||
}
|
}
|
||||||
// transfer data
|
// transfer data
|
||||||
n = count - 1;
|
spiRead(dst, count);
|
||||||
for (uint16_t i = 0; i < n; i++) {
|
|
||||||
while (!(SPSR & (1 << SPIF)));
|
|
||||||
dst[i] = SPDR;
|
|
||||||
SPDR = 0XFF;
|
|
||||||
}
|
|
||||||
// wait for last byte
|
|
||||||
while (!(SPSR & (1 << SPIF)));
|
|
||||||
dst[n] = SPDR;
|
|
||||||
|
|
||||||
#else // OPTIMIZE_HARDWARE_SPI
|
// discard CRC
|
||||||
|
spiRec();
|
||||||
// skip data before offset
|
spiRec();
|
||||||
for (;offset_ < offset; offset_++) {
|
chipSelectHigh();
|
||||||
spiRec();
|
|
||||||
}
|
|
||||||
// transfer data
|
|
||||||
for (uint16_t i = 0; i < count; i++) {
|
|
||||||
dst[i] = spiRec();
|
|
||||||
}
|
|
||||||
#endif // OPTIMIZE_HARDWARE_SPI
|
|
||||||
|
|
||||||
offset_ += count;
|
|
||||||
if (!partialBlockRead_ || offset_ >= 512) {
|
|
||||||
// read rest of data, checksum and set chip select high
|
|
||||||
readEnd();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -397,39 +428,55 @@ uint8_t Sd2Card::readData(uint32_t block,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/** Skip remaining data in a block when in partial block read mode. */
|
|
||||||
void Sd2Card::readEnd(void) {
|
|
||||||
if (inBlock_) {
|
|
||||||
// skip data and crc
|
|
||||||
#ifdef OPTIMIZE_HARDWARE_SPI
|
|
||||||
// optimize skip for hardware
|
|
||||||
SPDR = 0XFF;
|
|
||||||
while (offset_++ < 513) {
|
|
||||||
while (!(SPSR & (1 << SPIF)));
|
|
||||||
SPDR = 0XFF;
|
|
||||||
}
|
|
||||||
// wait for last crc byte
|
|
||||||
while (!(SPSR & (1 << SPIF)));
|
|
||||||
#else // OPTIMIZE_HARDWARE_SPI
|
|
||||||
while (offset_++ < 514) spiRec();
|
|
||||||
#endif // OPTIMIZE_HARDWARE_SPI
|
|
||||||
chipSelectHigh();
|
|
||||||
inBlock_ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
/** read CID or CSR register */
|
/** read CID or CSR register */
|
||||||
uint8_t Sd2Card::readRegister(uint8_t cmd, void* buf) {
|
bool Sd2Card::readRegister(uint8_t cmd, void* buf) {
|
||||||
uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
|
uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
|
||||||
if (cardCommand(cmd, 0)) {
|
if (cardCommand(cmd, 0)) {
|
||||||
error(SD_CARD_ERROR_READ_REG);
|
error(SD_CARD_ERROR_READ_REG);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (!waitStartBlock()) goto fail;
|
return readData(dst, 16);
|
||||||
// transfer data
|
|
||||||
for (uint16_t i = 0; i < 16; i++) dst[i] = spiRec();
|
fail:
|
||||||
spiRec(); // get first crc byte
|
chipSelectHigh();
|
||||||
spiRec(); // get second crc byte
|
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) {
|
||||||
|
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();
|
chipSelectHigh();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -450,50 +497,24 @@ uint8_t Sd2Card::readRegister(uint8_t cmd, void* buf) {
|
||||||
* \return The value one, true, is returned for success and the value zero,
|
* \return The value one, true, is returned for success and the value zero,
|
||||||
* false, is returned for an invalid value of \a sckRateID.
|
* false, is returned for an invalid value of \a sckRateID.
|
||||||
*/
|
*/
|
||||||
uint8_t Sd2Card::setSckRate(uint8_t sckRateID) {
|
bool Sd2Card::setSckRate(uint8_t sckRateID) {
|
||||||
if (sckRateID > 6) {
|
if (sckRateID > 6) {
|
||||||
error(SD_CARD_ERROR_SCK_RATE);
|
error(SD_CARD_ERROR_SCK_RATE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// see avr processor datasheet for SPI register bit definitions
|
spiRate_ = sckRateID;
|
||||||
if ((sckRateID & 1) || sckRateID == 6) {
|
|
||||||
SPSR &= ~(1 << SPI2X);
|
|
||||||
} else {
|
|
||||||
SPSR |= (1 << SPI2X);
|
|
||||||
}
|
|
||||||
SPCR &= ~((1 <<SPR1) | (1 << SPR0));
|
|
||||||
SPCR |= (sckRateID & 4 ? (1 << SPR1) : 0)
|
|
||||||
| (sckRateID & 2 ? (1 << SPR0) : 0);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// wait for card to go not busy
|
// wait for card to go not busy
|
||||||
uint8_t Sd2Card::waitNotBusy(uint16_t timeoutMillis) {
|
bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) {
|
||||||
uint16_t t0 = millis();
|
uint16_t t0 = millis();
|
||||||
do {
|
while (spiRec() != 0XFF) {
|
||||||
if (spiRec() == 0XFF) return true;
|
if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail;
|
||||||
}
|
|
||||||
while (((uint16_t)millis() - t0) < timeoutMillis);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
/** Wait for start block token */
|
|
||||||
uint8_t Sd2Card::waitStartBlock(void) {
|
|
||||||
uint16_t t0 = millis();
|
|
||||||
while ((status_ = spiRec()) == 0XFF) {
|
|
||||||
if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) {
|
|
||||||
error(SD_CARD_ERROR_READ_TIMEOUT);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (status_ != DATA_START_BLOCK) {
|
|
||||||
error(SD_CARD_ERROR_READ);
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
chipSelectHigh();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
@ -505,15 +526,7 @@ uint8_t Sd2Card::waitStartBlock(void) {
|
||||||
* \return The value one, true, is returned for success and
|
* \return The value one, true, is returned for success and
|
||||||
* the value zero, false, is returned for failure.
|
* the value zero, false, is returned for failure.
|
||||||
*/
|
*/
|
||||||
uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
|
bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
|
||||||
#if SD_PROTECT_BLOCK_ZERO
|
|
||||||
// don't allow write to first block
|
|
||||||
if (blockNumber == 0) {
|
|
||||||
error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
#endif // SD_PROTECT_BLOCK_ZERO
|
|
||||||
|
|
||||||
// use address if not SDHC card
|
// use address if not SDHC card
|
||||||
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
|
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
|
||||||
if (cardCommand(CMD24, blockNumber)) {
|
if (cardCommand(CMD24, blockNumber)) {
|
||||||
|
@ -540,51 +553,42 @@ uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/** Write one data block in a multiple block write sequence */
|
/** Write one data block in a multiple block write sequence
|
||||||
uint8_t Sd2Card::writeData(const uint8_t* src) {
|
* \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
|
// wait for previous write to finish
|
||||||
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
|
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
|
||||||
error(SD_CARD_ERROR_WRITE_MULTIPLE);
|
if (!writeData(WRITE_MULTIPLE_TOKEN, src)) goto fail;
|
||||||
chipSelectHigh();
|
chipSelectHigh();
|
||||||
return false;
|
return true;
|
||||||
}
|
|
||||||
return writeData(WRITE_MULTIPLE_TOKEN, src);
|
fail:
|
||||||
|
error(SD_CARD_ERROR_WRITE_MULTIPLE);
|
||||||
|
chipSelectHigh();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// send one block of data for write block or write multiple blocks
|
// send one block of data for write block or write multiple blocks
|
||||||
uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) {
|
bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
|
||||||
#ifdef OPTIMIZE_HARDWARE_SPI
|
spiSendBlock(token, src);
|
||||||
|
|
||||||
// send data - optimized loop
|
|
||||||
SPDR = token;
|
|
||||||
|
|
||||||
// send two byte per iteration
|
|
||||||
for (uint16_t i = 0; i < 512; i += 2) {
|
|
||||||
while (!(SPSR & (1 << SPIF)));
|
|
||||||
SPDR = src[i];
|
|
||||||
while (!(SPSR & (1 << SPIF)));
|
|
||||||
SPDR = src[i+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for last data byte
|
|
||||||
while (!(SPSR & (1 << SPIF)));
|
|
||||||
|
|
||||||
#else // OPTIMIZE_HARDWARE_SPI
|
|
||||||
spiSend(token);
|
|
||||||
for (uint16_t i = 0; i < 512; i++) {
|
|
||||||
spiSend(src[i]);
|
|
||||||
}
|
|
||||||
#endif // OPTIMIZE_HARDWARE_SPI
|
|
||||||
spiSend(0xff); // dummy crc
|
spiSend(0xff); // dummy crc
|
||||||
spiSend(0xff); // dummy crc
|
spiSend(0xff); // dummy crc
|
||||||
|
|
||||||
status_ = spiRec();
|
status_ = spiRec();
|
||||||
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
|
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
|
||||||
error(SD_CARD_ERROR_WRITE);
|
error(SD_CARD_ERROR_WRITE);
|
||||||
chipSelectHigh();
|
goto fail;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
chipSelectHigh();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/** Start a write multiple blocks sequence.
|
/** Start a write multiple blocks sequence.
|
||||||
|
@ -598,14 +602,7 @@ uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) {
|
||||||
* \return The value one, true, is returned for success and
|
* \return The value one, true, is returned for success and
|
||||||
* the value zero, false, is returned for failure.
|
* the value zero, false, is returned for failure.
|
||||||
*/
|
*/
|
||||||
uint8_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
|
bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
|
||||||
#if SD_PROTECT_BLOCK_ZERO
|
|
||||||
// don't allow write to first block
|
|
||||||
if (blockNumber == 0) {
|
|
||||||
error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
#endif // SD_PROTECT_BLOCK_ZERO
|
|
||||||
// send pre-erase count
|
// send pre-erase count
|
||||||
if (cardAcmd(ACMD23, eraseCount)) {
|
if (cardAcmd(ACMD23, eraseCount)) {
|
||||||
error(SD_CARD_ERROR_ACMD23);
|
error(SD_CARD_ERROR_ACMD23);
|
||||||
|
@ -617,6 +614,7 @@ uint8_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
|
||||||
error(SD_CARD_ERROR_CMD25);
|
error(SD_CARD_ERROR_CMD25);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
chipSelectHigh();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -629,7 +627,8 @@ uint8_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
|
||||||
* \return The value one, true, is returned for success and
|
* \return The value one, true, is returned for success and
|
||||||
* the value zero, false, is returned for failure.
|
* the value zero, false, is returned for failure.
|
||||||
*/
|
*/
|
||||||
uint8_t Sd2Card::writeStop(void) {
|
bool Sd2Card::writeStop() {
|
||||||
|
chipSelectLow();
|
||||||
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
|
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
|
||||||
spiSend(STOP_TRAN_TOKEN);
|
spiSend(STOP_TRAN_TOKEN);
|
||||||
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
|
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
|
||||||
|
|
253
Marlin/Sd2Card.h
253
Marlin/Sd2Card.h
|
@ -21,67 +21,24 @@
|
||||||
#define Sd2Card_h
|
#define Sd2Card_h
|
||||||
/**
|
/**
|
||||||
* \file
|
* \file
|
||||||
* Sd2Card class
|
* \brief Sd2Card class for V2 SD/SDHC cards
|
||||||
*/
|
*/
|
||||||
|
#include "SdFatConfig.h"
|
||||||
#include "Sd2PinMap.h"
|
#include "Sd2PinMap.h"
|
||||||
#include "SdInfo.h"
|
#include "SdInfo.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// SPI speed is F_CPU/2^(1 + index), 0 <= index <= 6
|
||||||
/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */
|
/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */
|
||||||
uint8_t const SPI_FULL_SPEED = 0;
|
uint8_t const SPI_FULL_SPEED = 0;
|
||||||
/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */
|
/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */
|
||||||
uint8_t const SPI_HALF_SPEED = 1;
|
uint8_t const SPI_HALF_SPEED = 1;
|
||||||
/** Set SCK rate to F_CPU/8. Sd2Card::setSckRate(). */
|
/** Set SCK rate to F_CPU/8. See Sd2Card::setSckRate(). */
|
||||||
uint8_t const SPI_QUARTER_SPEED = 2;
|
uint8_t const SPI_QUARTER_SPEED = 2;
|
||||||
/**
|
/** Set SCK rate to F_CPU/16. See Sd2Card::setSckRate(). */
|
||||||
* Define MEGA_SOFT_SPI non-zero to use software SPI on Mega Arduinos.
|
uint8_t const SPI_EIGHTH_SPEED = 3;
|
||||||
* Pins used are SS 10, MOSI 11, MISO 12, and SCK 13.
|
/** Set SCK rate to F_CPU/32. See Sd2Card::setSckRate(). */
|
||||||
*
|
uint8_t const SPI_SIXTEENTH_SPEED = 4;
|
||||||
* MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used
|
|
||||||
* on Mega Arduinos. Software SPI works well with GPS Shield V1.1
|
|
||||||
* but many SD cards will fail with GPS Shield V1.0.
|
|
||||||
*/
|
|
||||||
#define MEGA_SOFT_SPI 0
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__))
|
|
||||||
#define SOFTWARE_SPI
|
|
||||||
#endif // MEGA_SOFT_SPI
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// SPI pin definitions
|
|
||||||
//
|
|
||||||
#ifndef SOFTWARE_SPI
|
|
||||||
// hardware pin defs
|
|
||||||
/**
|
|
||||||
* SD Chip Select pin
|
|
||||||
*
|
|
||||||
* Warning if this pin is redefined the hardware SS will pin will be enabled
|
|
||||||
* as an output by init(). An avr processor will not function as an SPI
|
|
||||||
* master unless SS is set to output mode.
|
|
||||||
*/
|
|
||||||
/** The default chip select pin for the SD card is SS. */
|
|
||||||
uint8_t const SD_CHIP_SELECT_PIN = SS_PIN;
|
|
||||||
// The following three pins must not be redefined for hardware SPI.
|
|
||||||
/** SPI Master Out Slave In pin */
|
|
||||||
uint8_t const SPI_MOSI_PIN = MOSI_PIN;
|
|
||||||
/** SPI Master In Slave Out pin */
|
|
||||||
uint8_t const SPI_MISO_PIN = MISO_PIN;
|
|
||||||
/** SPI Clock pin */
|
|
||||||
uint8_t const SPI_SCK_PIN = SCK_PIN;
|
|
||||||
/** optimize loops for hardware SPI */
|
|
||||||
#define OPTIMIZE_HARDWARE_SPI
|
|
||||||
|
|
||||||
#else // SOFTWARE_SPI
|
|
||||||
// define software SPI pins so Mega can use unmodified GPS Shield
|
|
||||||
/** SPI chip select pin */
|
|
||||||
uint8_t const SD_CHIP_SELECT_PIN = 10;
|
|
||||||
/** SPI Master Out Slave In pin */
|
|
||||||
uint8_t const SPI_MOSI_PIN = 11;
|
|
||||||
/** SPI Master In Slave Out pin */
|
|
||||||
uint8_t const SPI_MISO_PIN = 12;
|
|
||||||
/** SPI Clock pin */
|
|
||||||
uint8_t const SPI_SCK_PIN = 13;
|
|
||||||
#endif // SOFTWARE_SPI
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
/** Protect block zero from write if nonzero */
|
|
||||||
#define SD_PROTECT_BLOCK_ZERO 1
|
|
||||||
/** init timeout ms */
|
/** init timeout ms */
|
||||||
uint16_t const SD_INIT_TIMEOUT = 2000;
|
uint16_t const SD_INIT_TIMEOUT = 2000;
|
||||||
/** erase timeout ms */
|
/** erase timeout ms */
|
||||||
|
@ -92,58 +49,99 @@ uint16_t const SD_READ_TIMEOUT = 300;
|
||||||
uint16_t const SD_WRITE_TIMEOUT = 600;
|
uint16_t const SD_WRITE_TIMEOUT = 600;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// SD card errors
|
// SD card errors
|
||||||
/** timeout error for command CMD0 */
|
/** timeout error for command CMD0 (initialize card in SPI mode) */
|
||||||
uint8_t const SD_CARD_ERROR_CMD0 = 0X1;
|
uint8_t const SD_CARD_ERROR_CMD0 = 0X1;
|
||||||
/** CMD8 was not accepted - not a valid SD card*/
|
/** CMD8 was not accepted - not a valid SD card*/
|
||||||
uint8_t const SD_CARD_ERROR_CMD8 = 0X2;
|
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) */
|
/** card returned an error response for CMD17 (read block) */
|
||||||
uint8_t const SD_CARD_ERROR_CMD17 = 0X3;
|
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) */
|
/** card returned an error response for CMD24 (write block) */
|
||||||
uint8_t const SD_CARD_ERROR_CMD24 = 0X4;
|
uint8_t const SD_CARD_ERROR_CMD24 = 0X6;
|
||||||
/** WRITE_MULTIPLE_BLOCKS command failed */
|
/** WRITE_MULTIPLE_BLOCKS command failed */
|
||||||
uint8_t const SD_CARD_ERROR_CMD25 = 0X05;
|
uint8_t const SD_CARD_ERROR_CMD25 = 0X7;
|
||||||
/** card returned an error response for CMD58 (read OCR) */
|
/** card returned an error response for CMD58 (read OCR) */
|
||||||
uint8_t const SD_CARD_ERROR_CMD58 = 0X06;
|
uint8_t const SD_CARD_ERROR_CMD58 = 0X8;
|
||||||
/** SET_WR_BLK_ERASE_COUNT failed */
|
/** SET_WR_BLK_ERASE_COUNT failed */
|
||||||
uint8_t const SD_CARD_ERROR_ACMD23 = 0X07;
|
uint8_t const SD_CARD_ERROR_ACMD23 = 0X9;
|
||||||
/** card's ACMD41 initialization process timeout */
|
/** ACMD41 initialization process timeout */
|
||||||
uint8_t const SD_CARD_ERROR_ACMD41 = 0X08;
|
uint8_t const SD_CARD_ERROR_ACMD41 = 0XA;
|
||||||
/** card returned a bad CSR version field */
|
/** card returned a bad CSR version field */
|
||||||
uint8_t const SD_CARD_ERROR_BAD_CSD = 0X09;
|
uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB;
|
||||||
/** erase block group command failed */
|
/** erase block group command failed */
|
||||||
uint8_t const SD_CARD_ERROR_ERASE = 0X0A;
|
uint8_t const SD_CARD_ERROR_ERASE = 0XC;
|
||||||
/** card not capable of single block erase */
|
/** card not capable of single block erase */
|
||||||
uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0X0B;
|
uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD;
|
||||||
/** Erase sequence timed out */
|
/** Erase sequence timed out */
|
||||||
uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0X0C;
|
uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE;
|
||||||
/** card returned an error token instead of read data */
|
/** card returned an error token instead of read data */
|
||||||
uint8_t const SD_CARD_ERROR_READ = 0X0D;
|
uint8_t const SD_CARD_ERROR_READ = 0XF;
|
||||||
/** read CID or CSD failed */
|
/** read CID or CSD failed */
|
||||||
uint8_t const SD_CARD_ERROR_READ_REG = 0X0E;
|
uint8_t const SD_CARD_ERROR_READ_REG = 0X10;
|
||||||
/** timeout while waiting for start of read data */
|
/** timeout while waiting for start of read data */
|
||||||
uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X0F;
|
uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11;
|
||||||
/** card did not accept STOP_TRAN_TOKEN */
|
/** card did not accept STOP_TRAN_TOKEN */
|
||||||
uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X10;
|
uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12;
|
||||||
/** card returned an error token as a response to a write operation */
|
/** card returned an error token as a response to a write operation */
|
||||||
uint8_t const SD_CARD_ERROR_WRITE = 0X11;
|
uint8_t const SD_CARD_ERROR_WRITE = 0X13;
|
||||||
/** attempt to write protected block zero */
|
/** attempt to write protected block zero */
|
||||||
uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X12;
|
uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14; // REMOVE - not used
|
||||||
/** card did not go ready for a multiple block write */
|
/** card did not go ready for a multiple block write */
|
||||||
uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X13;
|
uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15;
|
||||||
/** card returned an error to a CMD13 status check after a write */
|
/** card returned an error to a CMD13 status check after a write */
|
||||||
uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X14;
|
uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16;
|
||||||
/** timeout occurred during write programming */
|
/** timeout occurred during write programming */
|
||||||
uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X15;
|
uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17;
|
||||||
/** incorrect rate selected */
|
/** incorrect rate selected */
|
||||||
uint8_t const SD_CARD_ERROR_SCK_RATE = 0X16;
|
uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18;
|
||||||
|
/** init() not called */
|
||||||
|
uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// card types
|
// card types
|
||||||
/** Standard capacity V1 SD card */
|
/** Standard capacity V1 SD card */
|
||||||
uint8_t const SD_CARD_TYPE_SD1 = 1;
|
uint8_t const SD_CARD_TYPE_SD1 = 1;
|
||||||
/** Standard capacity V2 SD card */
|
/** Standard capacity V2 SD card */
|
||||||
uint8_t const SD_CARD_TYPE_SD2 = 2;
|
uint8_t const SD_CARD_TYPE_SD2 = 2;
|
||||||
/** High Capacity SD card */
|
/** High Capacity SD card */
|
||||||
uint8_t const SD_CARD_TYPE_SDHC = 3;
|
uint8_t const SD_CARD_TYPE_SDHC = 3;
|
||||||
|
/**
|
||||||
|
* define SOFTWARE_SPI to use bit-bang SPI
|
||||||
|
*/
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__))
|
||||||
|
#define SOFTWARE_SPI
|
||||||
|
#elif USE_SOFTWARE_SPI
|
||||||
|
#define SOFTWARE_SPI
|
||||||
|
#endif // MEGA_SOFT_SPI
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// SPI pin definitions - do not edit here - change in SdFatConfig.h
|
||||||
|
//
|
||||||
|
#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_PIN;
|
||||||
|
// The following three pins must not be redefined for hardware SPI.
|
||||||
|
/** SPI Master Out Slave In pin */
|
||||||
|
uint8_t const SPI_MOSI_PIN = MOSI_PIN;
|
||||||
|
/** SPI Master In Slave Out pin */
|
||||||
|
uint8_t const SPI_MISO_PIN = MISO_PIN;
|
||||||
|
/** SPI Clock pin */
|
||||||
|
uint8_t const SPI_SCK_PIN = SCK_PIN;
|
||||||
|
|
||||||
|
#else // SOFTWARE_SPI
|
||||||
|
|
||||||
|
/** SPI chip select pin */
|
||||||
|
uint8_t const SD_CHIP_SELECT_PIN = SOFT_SPI_CS_PIN;
|
||||||
|
/** SPI Master Out Slave In pin */
|
||||||
|
uint8_t const SPI_MOSI_PIN = SOFT_SPI_MOSI_PIN;
|
||||||
|
/** SPI Master In Slave Out pin */
|
||||||
|
uint8_t const SPI_MISO_PIN = SOFT_SPI_MISO_PIN;
|
||||||
|
/** SPI Clock pin */
|
||||||
|
uint8_t const SPI_SCK_PIN = SOFT_SPI_SCK_PIN;
|
||||||
|
#endif // SOFTWARE_SPI
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* \class Sd2Card
|
* \class Sd2Card
|
||||||
|
@ -152,66 +150,70 @@ uint8_t const SD_CARD_TYPE_SDHC = 3;
|
||||||
class Sd2Card {
|
class Sd2Card {
|
||||||
public:
|
public:
|
||||||
/** Construct an instance of Sd2Card. */
|
/** Construct an instance of Sd2Card. */
|
||||||
Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {}
|
Sd2Card() : errorCode_(SD_CARD_ERROR_INIT_NOT_CALLED), type_(0) {}
|
||||||
uint32_t cardSize(void);
|
uint32_t cardSize();
|
||||||
uint8_t erase(uint32_t firstBlock, uint32_t lastBlock);
|
bool erase(uint32_t firstBlock, uint32_t lastBlock);
|
||||||
uint8_t eraseSingleBlockEnable(void);
|
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.
|
* \return error code for last error. See Sd2Card.h for a list of error codes.
|
||||||
*/
|
*/
|
||||||
uint8_t errorCode(void) const {return errorCode_;}
|
int errorCode() const {return errorCode_;}
|
||||||
/** \return error data for last error. */
|
/** \return error data for last error. */
|
||||||
uint8_t errorData(void) const {return status_;}
|
int errorData() const {return status_;}
|
||||||
/**
|
/**
|
||||||
* Initialize an SD flash memory card with default clock rate and chip
|
* Initialize an SD flash memory card with default clock rate and chip
|
||||||
* select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
|
* select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
*/
|
*/
|
||||||
uint8_t init(void) {
|
bool init(uint8_t sckRateID = SPI_FULL_SPEED,
|
||||||
return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN);
|
uint8_t chipSelectPin = SD_CHIP_SELECT_PIN);
|
||||||
}
|
bool readBlock(uint32_t block, uint8_t* dst);
|
||||||
/**
|
/**
|
||||||
* Initialize an SD flash memory card with the selected SPI clock rate
|
* Read a card's CID register. The CID contains card identification
|
||||||
* and the default SD chip select pin.
|
|
||||||
* See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
|
|
||||||
*/
|
|
||||||
uint8_t init(uint8_t sckRateID) {
|
|
||||||
return init(sckRateID, SD_CHIP_SELECT_PIN);
|
|
||||||
}
|
|
||||||
uint8_t init(uint8_t sckRateID, uint8_t chipSelectPin);
|
|
||||||
void partialBlockRead(uint8_t value);
|
|
||||||
/** Returns the current value, true or false, for partial block read. */
|
|
||||||
uint8_t partialBlockRead(void) const {return partialBlockRead_;}
|
|
||||||
uint8_t readBlock(uint32_t block, uint8_t* dst);
|
|
||||||
uint8_t readData(uint32_t block,
|
|
||||||
uint16_t offset, uint16_t count, uint8_t* dst);
|
|
||||||
/**
|
|
||||||
* Read a cards CID register. The CID contains card identification
|
|
||||||
* information such as Manufacturer ID, Product name, Product serial
|
* information such as Manufacturer ID, Product name, Product serial
|
||||||
* number and Manufacturing date. */
|
* number and Manufacturing date.
|
||||||
uint8_t readCID(cid_t* cid) {
|
*
|
||||||
|
* \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);
|
return readRegister(CMD10, cid);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Read a cards CSD register. The CSD contains Card-Specific Data that
|
* Read a card's CSD register. The CSD contains Card-Specific Data that
|
||||||
* provides information regarding access to the card's contents. */
|
* provides information regarding access to the card's contents.
|
||||||
uint8_t readCSD(csd_t* csd) {
|
*
|
||||||
|
* \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);
|
return readRegister(CMD9, csd);
|
||||||
}
|
}
|
||||||
void readEnd(void);
|
bool readData(uint8_t *dst);
|
||||||
uint8_t setSckRate(uint8_t sckRateID);
|
bool readStart(uint32_t blockNumber);
|
||||||
/** Return the card type: SD V1, SD V2 or SDHC */
|
bool readStop();
|
||||||
uint8_t type(void) const {return type_;}
|
bool setSckRate(uint8_t sckRateID);
|
||||||
uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src);
|
/** Return the card type: SD V1, SD V2 or SDHC
|
||||||
uint8_t writeData(const uint8_t* src);
|
* \return 0 - SD V1, 1 - SD V2, or 3 - SDHC.
|
||||||
uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount);
|
*/
|
||||||
uint8_t writeStop(void);
|
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:
|
private:
|
||||||
uint32_t block_;
|
//----------------------------------------------------------------------------
|
||||||
uint8_t chipSelectPin_;
|
uint8_t chipSelectPin_;
|
||||||
uint8_t errorCode_;
|
uint8_t errorCode_;
|
||||||
uint8_t inBlock_;
|
uint8_t spiRate_;
|
||||||
uint16_t offset_;
|
|
||||||
uint8_t partialBlockRead_;
|
|
||||||
uint8_t status_;
|
uint8_t status_;
|
||||||
uint8_t type_;
|
uint8_t type_;
|
||||||
// private functions
|
// private functions
|
||||||
|
@ -220,14 +222,13 @@ class Sd2Card {
|
||||||
return cardCommand(cmd, arg);
|
return cardCommand(cmd, arg);
|
||||||
}
|
}
|
||||||
uint8_t cardCommand(uint8_t cmd, uint32_t arg);
|
uint8_t cardCommand(uint8_t cmd, uint32_t arg);
|
||||||
void error(uint8_t code) {errorCode_ = code;}
|
|
||||||
uint8_t readRegister(uint8_t cmd, void* buf);
|
bool readData(uint8_t* dst, uint16_t count);
|
||||||
uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount);
|
bool readRegister(uint8_t cmd, void* buf);
|
||||||
void chipSelectHigh(void);
|
void chipSelectHigh();
|
||||||
void chipSelectLow(void);
|
void chipSelectLow();
|
||||||
void type(uint8_t value) {type_ = value;}
|
void type(uint8_t value) {type_ = value;}
|
||||||
uint8_t waitNotBusy(uint16_t timeoutMillis);
|
bool waitNotBusy(uint16_t timeoutMillis);
|
||||||
uint8_t writeData(uint8_t token, const uint8_t* src);
|
bool writeData(uint8_t token, const uint8_t* src);
|
||||||
uint8_t waitStartBlock(void);
|
|
||||||
};
|
};
|
||||||
#endif // Sd2Card_h
|
#endif // Sd2Card_h
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#ifndef Sd2PinMap_h
|
#ifndef Sd2PinMap_h
|
||||||
#define Sd2PinMap_h
|
#define Sd2PinMap_h
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/** struct for mapping digital pins */
|
/** struct for mapping digital pins */
|
||||||
struct pin_map_t {
|
struct pin_map_t {
|
||||||
|
@ -31,18 +30,19 @@ struct pin_map_t {
|
||||||
uint8_t bit;
|
uint8_t bit;
|
||||||
};
|
};
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
#if defined(__AVR_ATmega1280__)\
|
||||||
|
|| defined(__AVR_ATmega2560__)
|
||||||
// Mega
|
// Mega
|
||||||
|
|
||||||
// Two Wire (aka I2C) ports
|
// Two Wire (aka I2C) ports
|
||||||
uint8_t const SDA_PIN = 20;
|
uint8_t const SDA_PIN = 20; // D1
|
||||||
uint8_t const SCL_PIN = 21;
|
uint8_t const SCL_PIN = 21; // D0
|
||||||
|
|
||||||
// SPI port
|
// SPI port
|
||||||
uint8_t const SS_PIN = 53;
|
uint8_t const SS_PIN = 53; // B0
|
||||||
uint8_t const MOSI_PIN = 51;
|
uint8_t const MOSI_PIN = 51; // B2
|
||||||
uint8_t const MISO_PIN = 50;
|
uint8_t const MISO_PIN = 50; // B3
|
||||||
uint8_t const SCK_PIN = 52;
|
uint8_t const SCK_PIN = 52; // B1
|
||||||
|
|
||||||
static const pin_map_t digitalPinMap[] = {
|
static const pin_map_t digitalPinMap[] = {
|
||||||
{&DDRE, &PINE, &PORTE, 0}, // E0 0
|
{&DDRE, &PINE, &PORTE, 0}, // E0 0
|
||||||
|
@ -117,18 +117,20 @@ static const pin_map_t digitalPinMap[] = {
|
||||||
{&DDRK, &PINK, &PORTK, 7} // K7 69
|
{&DDRK, &PINK, &PORTK, 7} // K7 69
|
||||||
};
|
};
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
|
#elif defined(__AVR_ATmega644P__)\
|
||||||
|
|| defined(__AVR_ATmega644__)\
|
||||||
|
|| defined(__AVR_ATmega1284P__)
|
||||||
// Sanguino
|
// Sanguino
|
||||||
|
|
||||||
// Two Wire (aka I2C) ports
|
// Two Wire (aka I2C) ports
|
||||||
uint8_t const SDA_PIN = 17;
|
uint8_t const SDA_PIN = 17; // C1
|
||||||
uint8_t const SCL_PIN = 18;
|
uint8_t const SCL_PIN = 18; // C2
|
||||||
|
|
||||||
// SPI port
|
// SPI port
|
||||||
uint8_t const SS_PIN = 4;
|
uint8_t const SS_PIN = 4; // B4
|
||||||
uint8_t const MOSI_PIN = 5;
|
uint8_t const MOSI_PIN = 5; // B5
|
||||||
uint8_t const MISO_PIN = 6;
|
uint8_t const MISO_PIN = 6; // B6
|
||||||
uint8_t const SCK_PIN = 7;
|
uint8_t const SCK_PIN = 7; // B7
|
||||||
|
|
||||||
static const pin_map_t digitalPinMap[] = {
|
static const pin_map_t digitalPinMap[] = {
|
||||||
{&DDRB, &PINB, &PORTB, 0}, // B0 0
|
{&DDRB, &PINB, &PORTB, 0}, // B0 0
|
||||||
|
@ -169,14 +171,14 @@ static const pin_map_t digitalPinMap[] = {
|
||||||
// Teensy 2.0
|
// Teensy 2.0
|
||||||
|
|
||||||
// Two Wire (aka I2C) ports
|
// Two Wire (aka I2C) ports
|
||||||
uint8_t const SDA_PIN = 6;
|
uint8_t const SDA_PIN = 6; // D1
|
||||||
uint8_t const SCL_PIN = 5;
|
uint8_t const SCL_PIN = 5; // D0
|
||||||
|
|
||||||
// SPI port
|
// SPI port
|
||||||
uint8_t const SS_PIN = 0;
|
uint8_t const SS_PIN = 0; // B0
|
||||||
uint8_t const MOSI_PIN = 2;
|
uint8_t const MOSI_PIN = 2; // B2
|
||||||
uint8_t const MISO_PIN = 3;
|
uint8_t const MISO_PIN = 3; // B3
|
||||||
uint8_t const SCK_PIN = 1;
|
uint8_t const SCK_PIN = 1; // B1
|
||||||
|
|
||||||
static const pin_map_t digitalPinMap[] = {
|
static const pin_map_t digitalPinMap[] = {
|
||||||
{&DDRB, &PINB, &PORTB, 0}, // B0 0
|
{&DDRB, &PINB, &PORTB, 0}, // B0 0
|
||||||
|
@ -206,18 +208,19 @@ static const pin_map_t digitalPinMap[] = {
|
||||||
{&DDRE, &PINE, &PORTE, 6} // E6 24
|
{&DDRE, &PINE, &PORTE, 6} // E6 24
|
||||||
};
|
};
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
|
#elif defined(__AVR_AT90USB646__)\
|
||||||
|
|| defined(__AVR_AT90USB1286__)
|
||||||
// Teensy++ 1.0 & 2.0
|
// Teensy++ 1.0 & 2.0
|
||||||
|
|
||||||
// Two Wire (aka I2C) ports
|
// Two Wire (aka I2C) ports
|
||||||
uint8_t const SDA_PIN = 1;
|
uint8_t const SDA_PIN = 1; // D1
|
||||||
uint8_t const SCL_PIN = 0;
|
uint8_t const SCL_PIN = 0; // D0
|
||||||
|
|
||||||
// SPI port
|
// SPI port
|
||||||
uint8_t const SS_PIN = 20;
|
uint8_t const SS_PIN = 20; // B0
|
||||||
uint8_t const MOSI_PIN = 22;
|
uint8_t const MOSI_PIN = 22; // B2
|
||||||
uint8_t const MISO_PIN = 23;
|
uint8_t const MISO_PIN = 23; // B3
|
||||||
uint8_t const SCK_PIN = 21;
|
uint8_t const SCK_PIN = 21; // B1
|
||||||
|
|
||||||
static const pin_map_t digitalPinMap[] = {
|
static const pin_map_t digitalPinMap[] = {
|
||||||
{&DDRD, &PIND, &PORTD, 0}, // D0 0
|
{&DDRD, &PIND, &PORTD, 0}, // D0 0
|
||||||
|
@ -268,18 +271,20 @@ static const pin_map_t digitalPinMap[] = {
|
||||||
{&DDRF, &PINF, &PORTF, 7} // F7 45
|
{&DDRF, &PINF, &PORTF, 7} // F7 45
|
||||||
};
|
};
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#else // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
#elif defined(__AVR_ATmega168__)\
|
||||||
|
||defined(__AVR_ATmega168P__)\
|
||||||
|
||defined(__AVR_ATmega328P__)
|
||||||
// 168 and 328 Arduinos
|
// 168 and 328 Arduinos
|
||||||
|
|
||||||
// Two Wire (aka I2C) ports
|
// Two Wire (aka I2C) ports
|
||||||
uint8_t const SDA_PIN = 18;
|
uint8_t const SDA_PIN = 18; // C4
|
||||||
uint8_t const SCL_PIN = 19;
|
uint8_t const SCL_PIN = 19; // C5
|
||||||
|
|
||||||
// SPI port
|
// SPI port
|
||||||
uint8_t const SS_PIN = 10;
|
uint8_t const SS_PIN = 10; // B2
|
||||||
uint8_t const MOSI_PIN = 11;
|
uint8_t const MOSI_PIN = 11; // B3
|
||||||
uint8_t const MISO_PIN = 12;
|
uint8_t const MISO_PIN = 12; // B4
|
||||||
uint8_t const SCK_PIN = 13;
|
uint8_t const SCK_PIN = 13; // B5
|
||||||
|
|
||||||
static const pin_map_t digitalPinMap[] = {
|
static const pin_map_t digitalPinMap[] = {
|
||||||
{&DDRD, &PIND, &PORTD, 0}, // D0 0
|
{&DDRD, &PIND, &PORTD, 0}, // D0 0
|
||||||
|
@ -303,7 +308,9 @@ static const pin_map_t digitalPinMap[] = {
|
||||||
{&DDRC, &PINC, &PORTC, 4}, // C4 18
|
{&DDRC, &PINC, &PORTC, 4}, // C4 18
|
||||||
{&DDRC, &PINC, &PORTC, 5} // C5 19
|
{&DDRC, &PINC, &PORTC, 5} // C5 19
|
||||||
};
|
};
|
||||||
#endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
#else // defined(__AVR_ATmega1280__)
|
||||||
|
#error unknown chip
|
||||||
|
#endif // defined(__AVR_ATmega1280__)
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
static const uint8_t digitalPinCount = sizeof(digitalPinMap)/sizeof(pin_map_t);
|
static const uint8_t digitalPinCount = sizeof(digitalPinMap)/sizeof(pin_map_t);
|
||||||
|
|
||||||
|
@ -311,7 +318,7 @@ uint8_t badPinNumber(void)
|
||||||
__attribute__((error("Pin number is too large or not a constant")));
|
__attribute__((error("Pin number is too large or not a constant")));
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
uint8_t getPinMode(uint8_t pin) {
|
bool getPinMode(uint8_t pin) {
|
||||||
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
|
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
|
||||||
return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1;
|
return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -331,7 +338,7 @@ static inline __attribute__((always_inline))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
uint8_t fastDigitalRead(uint8_t pin) {
|
bool fastDigitalRead(uint8_t pin) {
|
||||||
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
|
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
|
||||||
return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1;
|
return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1;
|
||||||
} else {
|
} else {
|
||||||
|
|
1818
Marlin/SdBaseFile.cpp
Normal file
1818
Marlin/SdBaseFile.cpp
Normal file
File diff suppressed because it is too large
Load diff
489
Marlin/SdBaseFile.h
Normal file
489
Marlin/SdBaseFile.h
Normal file
|
@ -0,0 +1,489 @@
|
||||||
|
/* Arduino SdFat Library
|
||||||
|
* Copyright (C) 2009 by William Greiman
|
||||||
|
*
|
||||||
|
* This file is part of the Arduino SdFat Library
|
||||||
|
*
|
||||||
|
* This Library is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This Library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with the Arduino SdFat Library. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef SdBaseFile_h
|
||||||
|
#define SdBaseFile_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief SdBaseFile class
|
||||||
|
*/
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#if ARDUINO < 100
|
||||||
|
#include <WProgram.h>
|
||||||
|
#else // ARDUINO
|
||||||
|
#include <Arduino.h>
|
||||||
|
#endif // ARDUINO
|
||||||
|
#include "SdFatConfig.h"
|
||||||
|
#include "SdVolume.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* \struct fpos_t
|
||||||
|
* \brief internal type for istream
|
||||||
|
* do not use in user apps
|
||||||
|
*/
|
||||||
|
struct fpos_t {
|
||||||
|
/** stream position */
|
||||||
|
uint32_t position;
|
||||||
|
/** cluster for position */
|
||||||
|
uint32_t cluster;
|
||||||
|
fpos_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);
|
||||||
|
~SdBaseFile() {if(isOpen()) close();}
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// helpers for stream classes
|
||||||
|
/** get position for streams
|
||||||
|
* \param[out] pos struct to receive position
|
||||||
|
*/
|
||||||
|
void getpos(fpos_t* pos);
|
||||||
|
/** set position for streams
|
||||||
|
* \param[out] pos struct with value for new position
|
||||||
|
*/
|
||||||
|
void setpos(fpos_t* pos);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
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();
|
||||||
|
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);
|
||||||
|
bool printName();
|
||||||
|
int16_t read();
|
||||||
|
int16_t read(void* buf, uint16_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_;}
|
||||||
|
int16_t write(const void* buf, uint16_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
|
||||||
|
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
|
||||||
|
uint8_t dirIndex_; // index of directory entry in dirBlock
|
||||||
|
uint32_t fileSize_; // file size in bytes
|
||||||
|
uint32_t firstCluster_; // first cluster of file
|
||||||
|
SdVolume* vol_; // volume where file is located
|
||||||
|
|
||||||
|
/** experimental don't use */
|
||||||
|
bool openParent(SdBaseFile* dir);
|
||||||
|
// private functions
|
||||||
|
bool addCluster();
|
||||||
|
bool 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();
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
#endif // ALLOW_DEPRECATED_FUNCTIONS
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SdBaseFile_h
|
329
Marlin/SdFat.cpp
Normal file
329
Marlin/SdFat.cpp
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
/* Arduino SdFat Library
|
||||||
|
* Copyright (C) 2009 by William Greiman
|
||||||
|
*
|
||||||
|
* This file is part of the Arduino SdFat Library
|
||||||
|
*
|
||||||
|
* This Library is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This Library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with the Arduino SdFat Library. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "SdFat.h"
|
||||||
|
#include "SdFatUtil.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** 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_;
|
||||||
|
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 \<drive letter>: 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;
|
||||||
|
PgmPrint("SD errorCode: 0X");
|
||||||
|
Serial.println(card_.errorCode(), HEX);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** %Print msg, any SD error code.
|
||||||
|
*
|
||||||
|
* \param[in] msg Message to print.
|
||||||
|
*/
|
||||||
|
void SdFat::errorPrint(char const* msg) {
|
||||||
|
PgmPrint("error: ");
|
||||||
|
Serial.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) {
|
||||||
|
PgmPrint("error: ");
|
||||||
|
SerialPrintln_P(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);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* 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 SdFat::init(uint8_t sckRateID, uint8_t chipSelectPin) {
|
||||||
|
return card_.init(sckRateID, chipSelectPin) && vol_.init(&card_) && chdir(1);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** %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) {
|
||||||
|
Serial.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) {
|
||||||
|
SerialPrintln_P(msg);
|
||||||
|
initErrorHalt();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** Print error details after SdFat::init() fails. */
|
||||||
|
void SdFat::initErrorPrint() {
|
||||||
|
if (card_.errorCode()) {
|
||||||
|
PgmPrintln("Can't access SD card. Do not reformat.");
|
||||||
|
if (card_.errorCode() == SD_CARD_ERROR_CMD0) {
|
||||||
|
PgmPrintln("No card, wrong chip select pin, or SPI problem?");
|
||||||
|
}
|
||||||
|
errorPrint();
|
||||||
|
} else if (vol_.fatType() == 0) {
|
||||||
|
PgmPrintln("Invalid format, reformat SD.");
|
||||||
|
} else if (!vwd_.isOpen()) {
|
||||||
|
PgmPrintln("Can't open root directory.");
|
||||||
|
} else {
|
||||||
|
PgmPrintln("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) {
|
||||||
|
Serial.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) {
|
||||||
|
SerialPrintln_P(msg);
|
||||||
|
initErrorHalt();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** List the directory contents of the volume working directory to Serial.
|
||||||
|
*
|
||||||
|
* \param[in] flags The inclusive OR of
|
||||||
|
*
|
||||||
|
* LS_DATE - %Print file modification date
|
||||||
|
*
|
||||||
|
* LS_SIZE - %Print file size.
|
||||||
|
*
|
||||||
|
* LS_R - Recursive list of subdirectories.
|
||||||
|
*/
|
||||||
|
void SdFat::ls(uint8_t flags) {
|
||||||
|
vwd_.ls(&Serial, flags);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** List the directory contents of the volume working directory to Serial.
|
||||||
|
*
|
||||||
|
* \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);
|
||||||
|
}
|
561
Marlin/SdFat.h
561
Marlin/SdFat.h
|
@ -21,527 +21,56 @@
|
||||||
#define SdFat_h
|
#define SdFat_h
|
||||||
/**
|
/**
|
||||||
* \file
|
* \file
|
||||||
* SdFile and SdVolume classes
|
* \brief SdFat class
|
||||||
*/
|
*/
|
||||||
#include <avr/pgmspace.h>
|
#include "SdFile.h"
|
||||||
#include "Sd2Card.h"
|
//#include <SdStream.h>
|
||||||
#include "FatStructs.h"
|
//#include <ArduinoStream.h>
|
||||||
#include "Print.h"
|
//------------------------------------------------------------------------------
|
||||||
|
/** SdFat version YYYYMMDD */
|
||||||
|
#define SD_FAT_VERSION 20110902
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Allow use of deprecated functions if non-zero
|
* \class SdFat
|
||||||
|
* \brief Integration class for the %SdFat library.
|
||||||
*/
|
*/
|
||||||
#define ALLOW_DEPRECATED_FUNCTIONS 1
|
class SdFat {
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// forward declaration since SdVolume is used in SdFile
|
|
||||||
class SdVolume;
|
|
||||||
//==============================================================================
|
|
||||||
// SdFile class
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// use the gnu style oflag in open()
|
|
||||||
/** open() oflag for reading */
|
|
||||||
uint8_t const O_READ = 0X01;
|
|
||||||
/** open() oflag - same as O_READ */
|
|
||||||
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;
|
|
||||||
/** create the file if nonexistent */
|
|
||||||
uint8_t const O_CREAT = 0X10;
|
|
||||||
/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */
|
|
||||||
uint8_t const O_EXCL = 0X20;
|
|
||||||
/** truncate the file to zero length */
|
|
||||||
uint8_t const O_TRUNC = 0X40;
|
|
||||||
|
|
||||||
// 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 SdFile has not been opened. */
|
|
||||||
uint8_t const FAT_FILE_TYPE_CLOSED = 0;
|
|
||||||
/** SdFile for a file */
|
|
||||||
uint8_t const FAT_FILE_TYPE_NORMAL = 1;
|
|
||||||
/** SdFile for a FAT16 root directory */
|
|
||||||
uint8_t const FAT_FILE_TYPE_ROOT16 = 2;
|
|
||||||
/** SdFile for a FAT32 root directory */
|
|
||||||
uint8_t const FAT_FILE_TYPE_ROOT32 = 3;
|
|
||||||
/** SdFile for a subdirectory */
|
|
||||||
uint8_t const FAT_FILE_TYPE_SUBDIR = 4;
|
|
||||||
/** Test value for directory type */
|
|
||||||
uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT16;
|
|
||||||
|
|
||||||
/** date field for FAT directory 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 */
|
|
||||||
static inline uint16_t FAT_YEAR(uint16_t fatDate) {
|
|
||||||
return 1980 + (fatDate >> 9);
|
|
||||||
}
|
|
||||||
/** month part of FAT directory date field */
|
|
||||||
static inline uint8_t FAT_MONTH(uint16_t fatDate) {
|
|
||||||
return (fatDate >> 5) & 0XF;
|
|
||||||
}
|
|
||||||
/** day part of FAT directory date field */
|
|
||||||
static inline uint8_t FAT_DAY(uint16_t fatDate) {
|
|
||||||
return fatDate & 0X1F;
|
|
||||||
}
|
|
||||||
/** time field for FAT directory entry */
|
|
||||||
static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) {
|
|
||||||
return hour << 11 | minute << 5 | second >> 1;
|
|
||||||
}
|
|
||||||
/** hour part of FAT directory time field */
|
|
||||||
static inline uint8_t FAT_HOUR(uint16_t fatTime) {
|
|
||||||
return fatTime >> 11;
|
|
||||||
}
|
|
||||||
/** minute part of FAT directory time field */
|
|
||||||
static inline uint8_t FAT_MINUTE(uint16_t fatTime) {
|
|
||||||
return(fatTime >> 5) & 0X3F;
|
|
||||||
}
|
|
||||||
/** second part of FAT directory time field */
|
|
||||||
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 SdFile
|
|
||||||
* \brief Access FAT16 and FAT32 files on SD and SDHC cards.
|
|
||||||
*/
|
|
||||||
class SdFile : public Print {
|
|
||||||
public:
|
public:
|
||||||
/** Create an instance of SdFile. */
|
SdFat() {}
|
||||||
SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {}
|
/** \return a pointer to the Sd2Card object. */
|
||||||
/**
|
Sd2Card* card() {return &card_;}
|
||||||
* writeError is set to true if an error occurs during a write().
|
bool chdir(bool set_cwd = false);
|
||||||
* Set writeError to false before calling print() and/or write() and check
|
bool chdir(const char* path, bool set_cwd = false);
|
||||||
* for true after calls to print() and/or write().
|
void chvol();
|
||||||
*/
|
void errorHalt();
|
||||||
bool writeError;
|
void errorHalt_P(PGM_P msg);
|
||||||
/**
|
void errorHalt(char const *msg);
|
||||||
* Cancel unbuffered reads for this file.
|
void errorPrint();
|
||||||
* See setUnbufferedRead()
|
void errorPrint_P(PGM_P msg);
|
||||||
*/
|
void errorPrint(char const *msg);
|
||||||
void clearUnbufferedRead(void) {
|
bool exists(const char* name);
|
||||||
flags_ &= ~F_FILE_UNBUFFERED_READ;
|
bool init(uint8_t sckRateID = SPI_FULL_SPEED,
|
||||||
}
|
uint8_t chipSelectPin = SD_CHIP_SELECT_PIN);
|
||||||
uint8_t close(void);
|
void initErrorHalt();
|
||||||
uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
|
void initErrorHalt(char const *msg);
|
||||||
uint8_t createContiguous(SdFile* dirFile,
|
void initErrorHalt_P(PGM_P msg);
|
||||||
const char* fileName, uint32_t size);
|
void initErrorPrint();
|
||||||
/** \return The current cluster number for a file or directory. */
|
void initErrorPrint(char const *msg);
|
||||||
uint32_t curCluster(void) const {return curCluster_;}
|
void initErrorPrint_P(PGM_P msg);
|
||||||
/** \return The current position for a file or directory. */
|
void ls(uint8_t flags = 0);
|
||||||
uint32_t curPosition(void) const {return curPosition_;}
|
void ls(Print* pr, uint8_t flags = 0);
|
||||||
/**
|
bool mkdir(const char* path, bool pFlag = true);
|
||||||
* Set the date/time callback function
|
bool remove(const char* path);
|
||||||
*
|
bool rename(const char *oldPath, const char *newPath);
|
||||||
* \param[in] dateTime The user's call back function. The callback
|
bool rmdir(const char* path);
|
||||||
* function is of the form:
|
bool truncate(const char* path, uint32_t length);
|
||||||
*
|
/** \return a pointer to the SdVolume object. */
|
||||||
* \code
|
SdVolume* vol() {return &vol_;}
|
||||||
* void dateTime(uint16_t* date, uint16_t* time) {
|
/** \return a pointer to the volume working directory. */
|
||||||
* uint16_t year;
|
SdBaseFile* vwd() {return &vwd_;}
|
||||||
* uint8_t month, day, hour, minute, second;
|
|
||||||
*
|
|
||||||
* // User gets date and time from GPS or real-time clock here
|
|
||||||
*
|
|
||||||
* // return date using FAT_DATE macro to format fields
|
|
||||||
* *date = FAT_DATE(year, month, day);
|
|
||||||
*
|
|
||||||
* // return time using FAT_TIME macro to format fields
|
|
||||||
* *time = FAT_TIME(hour, minute, second);
|
|
||||||
* }
|
|
||||||
* \endcode
|
|
||||||
*
|
|
||||||
* Sets the function that is called when a file is created or when
|
|
||||||
* a file's directory entry is modified by sync(). All timestamps,
|
|
||||||
* access, creation, and modify, are set when a file is created.
|
|
||||||
* sync() maintains the last access date and last modify date/time.
|
|
||||||
*
|
|
||||||
* See the timestamp() function.
|
|
||||||
*/
|
|
||||||
static void dateTimeCallback(
|
|
||||||
void (*dateTime)(uint16_t* date, uint16_t* time)) {
|
|
||||||
dateTime_ = dateTime;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Cancel the date/time callback function.
|
|
||||||
*/
|
|
||||||
static void dateTimeCallbackCancel(void) {
|
|
||||||
// use explicit zero since NULL is not defined for Sanguino
|
|
||||||
dateTime_ = 0;
|
|
||||||
}
|
|
||||||
/** \return Address of the block that contains this file's directory. */
|
|
||||||
uint32_t dirBlock(void) const {return dirBlock_;}
|
|
||||||
uint8_t dirEntry(dir_t* dir);
|
|
||||||
/** \return Index of this file's directory in the block dirBlock. */
|
|
||||||
uint8_t dirIndex(void) const {return dirIndex_;}
|
|
||||||
static void dirName(const dir_t& dir, char* name);
|
|
||||||
/** \return The total number of bytes in a file or directory. */
|
|
||||||
uint32_t fileSize(void) const {return fileSize_;}
|
|
||||||
/** \return The first cluster number for a file or directory. */
|
|
||||||
uint32_t firstCluster(void) const {return firstCluster_;}
|
|
||||||
/** \return True if this is a SdFile for a directory else false. */
|
|
||||||
uint8_t isDir(void) const {return type_ >= FAT_FILE_TYPE_MIN_DIR;}
|
|
||||||
/** \return True if this is a SdFile for a file else false. */
|
|
||||||
uint8_t isFile(void) const {return type_ == FAT_FILE_TYPE_NORMAL;}
|
|
||||||
/** \return True if this is a SdFile for an open file/directory else false. */
|
|
||||||
uint8_t isOpen(void) const {return type_ != FAT_FILE_TYPE_CLOSED;}
|
|
||||||
/** \return True if this is a SdFile for a subdirectory else false. */
|
|
||||||
uint8_t isSubDir(void) const {return type_ == FAT_FILE_TYPE_SUBDIR;}
|
|
||||||
/** \return True if this is a SdFile for the root directory. */
|
|
||||||
uint8_t isRoot(void) const {
|
|
||||||
return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32;
|
|
||||||
}
|
|
||||||
void ls(uint8_t flags = 0, uint8_t indent = 0);
|
|
||||||
uint8_t makeDir(SdFile* dir, const char* dirName);
|
|
||||||
uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag);
|
|
||||||
uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag);
|
|
||||||
|
|
||||||
uint8_t openRoot(SdVolume* vol);
|
|
||||||
static void printDirName(const dir_t& dir, uint8_t width);
|
|
||||||
static void printFatDate(uint16_t fatDate);
|
|
||||||
static void printFatTime(uint16_t fatTime);
|
|
||||||
static void printTwoDigits(uint8_t v);
|
|
||||||
/**
|
|
||||||
* Read the next byte from a file.
|
|
||||||
*
|
|
||||||
* \return For success read returns the next byte in the file as an int.
|
|
||||||
* If an error occurs or end of file is reached -1 is returned.
|
|
||||||
*/
|
|
||||||
int16_t read(void) {
|
|
||||||
uint8_t b;
|
|
||||||
return read(&b, 1) == 1 ? b : -1;
|
|
||||||
}
|
|
||||||
int16_t read(void* buf, uint16_t nbyte);
|
|
||||||
int8_t readDir(dir_t* dir);
|
|
||||||
static uint8_t remove(SdFile* dirFile, const char* fileName);
|
|
||||||
uint8_t remove(void);
|
|
||||||
/** Set the file's current position to zero. */
|
|
||||||
void rewind(void) {
|
|
||||||
curPosition_ = curCluster_ = 0;
|
|
||||||
}
|
|
||||||
uint8_t rmDir(void);
|
|
||||||
uint8_t rmRfStar(void);
|
|
||||||
/** Set the files position to current position + \a pos. See seekSet(). */
|
|
||||||
uint8_t seekCur(uint32_t pos) {
|
|
||||||
return seekSet(curPosition_ + pos);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Set the files current position to end of file. Useful to position
|
|
||||||
* a file for append. See seekSet().
|
|
||||||
*/
|
|
||||||
uint8_t seekEnd(void) {return seekSet(fileSize_);}
|
|
||||||
uint8_t seekSet(uint32_t pos);
|
|
||||||
/**
|
|
||||||
* Use unbuffered reads to access this file. Used with Wave
|
|
||||||
* Shield ISR. Used with Sd2Card::partialBlockRead() in WaveRP.
|
|
||||||
*
|
|
||||||
* Not recommended for normal applications.
|
|
||||||
*/
|
|
||||||
void setUnbufferedRead(void) {
|
|
||||||
if (isFile()) flags_ |= F_FILE_UNBUFFERED_READ;
|
|
||||||
}
|
|
||||||
uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day,
|
|
||||||
uint8_t hour, uint8_t minute, uint8_t second);
|
|
||||||
uint8_t sync(void);
|
|
||||||
/** Type of this SdFile. You should use isFile() or isDir() instead of type()
|
|
||||||
* if possible.
|
|
||||||
*
|
|
||||||
* \return The file or directory type.
|
|
||||||
*/
|
|
||||||
uint8_t type(void) const {return type_;}
|
|
||||||
uint8_t truncate(uint32_t size);
|
|
||||||
/** \return Unbuffered read flag. */
|
|
||||||
uint8_t unbufferedRead(void) const {
|
|
||||||
return flags_ & F_FILE_UNBUFFERED_READ;
|
|
||||||
}
|
|
||||||
/** \return SdVolume that contains this file. */
|
|
||||||
SdVolume* volume(void) const {return vol_;}
|
|
||||||
void write(uint8_t b);
|
|
||||||
int16_t write(const void* buf, uint16_t nbyte);
|
|
||||||
void write(const char* str);
|
|
||||||
void write_P(PGM_P str);
|
|
||||||
void writeln_P(PGM_P str);
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
#if ALLOW_DEPRECATED_FUNCTIONS
|
|
||||||
// Deprecated functions - suppress cpplint warnings with NOLINT comment
|
|
||||||
/** \deprecated Use:
|
|
||||||
* uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
|
|
||||||
*/
|
|
||||||
uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT
|
|
||||||
return contiguousRange(&bgnBlock, &endBlock);
|
|
||||||
}
|
|
||||||
/** \deprecated Use:
|
|
||||||
* uint8_t SdFile::createContiguous(SdFile* dirFile,
|
|
||||||
* const char* fileName, uint32_t size)
|
|
||||||
*/
|
|
||||||
uint8_t createContiguous(SdFile& dirFile, // NOLINT
|
|
||||||
const char* fileName, uint32_t size) {
|
|
||||||
return createContiguous(&dirFile, fileName, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \deprecated Use:
|
|
||||||
* static void SdFile::dateTimeCallback(
|
|
||||||
* void (*dateTime)(uint16_t* date, uint16_t* time));
|
|
||||||
*/
|
|
||||||
static void dateTimeCallback(
|
|
||||||
void (*dateTime)(uint16_t& date, uint16_t& time)) { // NOLINT
|
|
||||||
oldDateTime_ = dateTime;
|
|
||||||
dateTime_ = dateTime ? oldToNew : 0;
|
|
||||||
}
|
|
||||||
/** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */
|
|
||||||
uint8_t dirEntry(dir_t& dir) {return dirEntry(&dir);} // NOLINT
|
|
||||||
/** \deprecated Use:
|
|
||||||
* uint8_t SdFile::makeDir(SdFile* dir, const char* dirName);
|
|
||||||
*/
|
|
||||||
uint8_t makeDir(SdFile& dir, const char* dirName) { // NOLINT
|
|
||||||
return makeDir(&dir, dirName);
|
|
||||||
}
|
|
||||||
/** \deprecated Use:
|
|
||||||
* uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag);
|
|
||||||
*/
|
|
||||||
uint8_t open(SdFile& dirFile, // NOLINT
|
|
||||||
const char* fileName, uint8_t oflag) {
|
|
||||||
return open(&dirFile, fileName, oflag);
|
|
||||||
}
|
|
||||||
/** \deprecated Do not use in new apps */
|
|
||||||
uint8_t open(SdFile& dirFile, const char* fileName) { // NOLINT
|
|
||||||
return open(dirFile, fileName, O_RDWR);
|
|
||||||
}
|
|
||||||
/** \deprecated Use:
|
|
||||||
* uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag);
|
|
||||||
*/
|
|
||||||
uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT
|
|
||||||
return open(&dirFile, index, oflag);
|
|
||||||
}
|
|
||||||
/** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */
|
|
||||||
uint8_t openRoot(SdVolume& vol) {return openRoot(&vol);} // NOLINT
|
|
||||||
|
|
||||||
/** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */
|
|
||||||
int8_t readDir(dir_t& dir) {return readDir(&dir);} // NOLINT
|
|
||||||
/** \deprecated Use:
|
|
||||||
* static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName);
|
|
||||||
*/
|
|
||||||
static uint8_t remove(SdFile& dirFile, const char* fileName) { // NOLINT
|
|
||||||
return remove(&dirFile, fileName);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// rest are private
|
|
||||||
private:
|
private:
|
||||||
static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT
|
Sd2Card card_;
|
||||||
static void oldToNew(uint16_t* date, uint16_t* time) {
|
SdVolume vol_;
|
||||||
uint16_t d;
|
SdBaseFile vwd_;
|
||||||
uint16_t t;
|
|
||||||
oldDateTime_(d, t);
|
|
||||||
*date = d;
|
|
||||||
*time = t;
|
|
||||||
}
|
|
||||||
#endif // ALLOW_DEPRECATED_FUNCTIONS
|
|
||||||
private:
|
|
||||||
// bits defined in flags_
|
|
||||||
// should be 0XF
|
|
||||||
static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC);
|
|
||||||
// available bits
|
|
||||||
static uint8_t const F_UNUSED = 0X30;
|
|
||||||
// use unbuffered SD read
|
|
||||||
static uint8_t const F_FILE_UNBUFFERED_READ = 0X40;
|
|
||||||
// sync of directory entry required
|
|
||||||
static uint8_t const F_FILE_DIR_DIRTY = 0X80;
|
|
||||||
|
|
||||||
// make sure F_OFLAG is ok
|
|
||||||
#if ((F_UNUSED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG)
|
|
||||||
#error flags_ bits conflict
|
|
||||||
#endif // flags_ bits
|
|
||||||
|
|
||||||
// private data
|
|
||||||
uint8_t flags_; // See above for definition of flags_ bits
|
|
||||||
uint8_t type_; // type of file see above for values
|
|
||||||
uint32_t curCluster_; // cluster for current file position
|
|
||||||
uint32_t curPosition_; // current file position in bytes from beginning
|
|
||||||
uint32_t dirBlock_; // SD block that contains directory entry for file
|
|
||||||
uint8_t dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF
|
|
||||||
uint32_t fileSize_; // file size in bytes
|
|
||||||
uint32_t firstCluster_; // first cluster of file
|
|
||||||
SdVolume* vol_; // volume where file is located
|
|
||||||
|
|
||||||
// private functions
|
|
||||||
uint8_t addCluster(void);
|
|
||||||
uint8_t addDirCluster(void);
|
|
||||||
dir_t* cacheDirEntry(uint8_t action);
|
|
||||||
static void (*dateTime_)(uint16_t* date, uint16_t* time);
|
|
||||||
static uint8_t make83Name(const char* str, uint8_t* name);
|
|
||||||
uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
|
|
||||||
dir_t* readDirCache(void);
|
|
||||||
};
|
|
||||||
//==============================================================================
|
|
||||||
// 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 MasterBoot Record. */
|
|
||||||
mbr_t mbr;
|
|
||||||
/** Used to access to a cached FAT boot sector. */
|
|
||||||
fbs_t fbs;
|
|
||||||
};
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* \class SdVolume
|
|
||||||
* \brief Access FAT16 and FAT32 volumes on SD and SDHC cards.
|
|
||||||
*/
|
|
||||||
class SdVolume {
|
|
||||||
public:
|
|
||||||
/** Create an instance of SdVolume */
|
|
||||||
SdVolume(void) :allocSearchStart_(2), fatType_(0) {}
|
|
||||||
/** Clear the cache and returns a pointer to the cache. Used by the WaveRP
|
|
||||||
* recorder to do raw write to the SD card. Not for normal apps.
|
|
||||||
*/
|
|
||||||
static uint8_t* cacheClear(void) {
|
|
||||||
cacheFlush();
|
|
||||||
cacheBlockNumber_ = 0XFFFFFFFF;
|
|
||||||
return cacheBuffer_.data;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Initialize a FAT volume. Try partition one first then try super
|
|
||||||
* floppy format.
|
|
||||||
*
|
|
||||||
* \param[in] dev The Sd2Card where the volume is located.
|
|
||||||
*
|
|
||||||
* \return The value one, true, is returned for success and
|
|
||||||
* the value zero, false, is returned for failure. Reasons for
|
|
||||||
* failure include not finding a valid partition, not finding a valid
|
|
||||||
* FAT file system or an I/O error.
|
|
||||||
*/
|
|
||||||
uint8_t init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);}
|
|
||||||
uint8_t init(Sd2Card* dev, uint8_t part);
|
|
||||||
|
|
||||||
// inline functions that return volume info
|
|
||||||
/** \return The volume's cluster size in blocks. */
|
|
||||||
uint8_t blocksPerCluster(void) const {return blocksPerCluster_;}
|
|
||||||
/** \return The number of blocks in one FAT. */
|
|
||||||
uint32_t blocksPerFat(void) const {return blocksPerFat_;}
|
|
||||||
/** \return The total number of clusters in the volume. */
|
|
||||||
uint32_t clusterCount(void) const {return clusterCount_;}
|
|
||||||
/** \return The shift count required to multiply by blocksPerCluster. */
|
|
||||||
uint8_t clusterSizeShift(void) const {return clusterSizeShift_;}
|
|
||||||
/** \return The logical block number for the start of file data. */
|
|
||||||
uint32_t dataStartBlock(void) const {return dataStartBlock_;}
|
|
||||||
/** \return The number of FAT structures on the volume. */
|
|
||||||
uint8_t fatCount(void) const {return fatCount_;}
|
|
||||||
/** \return The logical block number for the start of the first FAT. */
|
|
||||||
uint32_t fatStartBlock(void) const {return fatStartBlock_;}
|
|
||||||
/** \return The FAT type of the volume. Values are 12, 16 or 32. */
|
|
||||||
uint8_t fatType(void) const {return fatType_;}
|
|
||||||
/** \return The number of entries in the root directory for FAT16 volumes. */
|
|
||||||
uint32_t rootDirEntryCount(void) const {return rootDirEntryCount_;}
|
|
||||||
/** \return The logical block number for the start of the root directory
|
|
||||||
on FAT16 volumes or the first cluster number on FAT32 volumes. */
|
|
||||||
uint32_t rootDirStart(void) const {return rootDirStart_;}
|
|
||||||
/** return a pointer to the Sd2Card object for this volume */
|
|
||||||
static Sd2Card* sdCard(void) {return sdCard_;}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
#if ALLOW_DEPRECATED_FUNCTIONS
|
|
||||||
// Deprecated functions - suppress cpplint warnings with NOLINT comment
|
|
||||||
/** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */
|
|
||||||
uint8_t init(Sd2Card& dev) {return init(&dev);} // NOLINT
|
|
||||||
|
|
||||||
/** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */
|
|
||||||
uint8_t init(Sd2Card& dev, uint8_t part) { // NOLINT
|
|
||||||
return init(&dev, part);
|
|
||||||
}
|
|
||||||
#endif // ALLOW_DEPRECATED_FUNCTIONS
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
// Allow SdFile access to SdVolume private data.
|
|
||||||
friend class SdFile;
|
|
||||||
|
|
||||||
// value for action argument in cacheRawBlock to indicate read from cache
|
|
||||||
static uint8_t const CACHE_FOR_READ = 0;
|
|
||||||
// value for action argument in cacheRawBlock to indicate cache dirty
|
|
||||||
static uint8_t const CACHE_FOR_WRITE = 1;
|
|
||||||
|
|
||||||
static cache_t cacheBuffer_; // 512 byte cache for device blocks
|
|
||||||
static uint32_t cacheBlockNumber_; // Logical number of block in the cache
|
|
||||||
static Sd2Card* sdCard_; // Sd2Card object for cache
|
|
||||||
static uint8_t cacheDirty_; // cacheFlush() will write block if true
|
|
||||||
static uint32_t cacheMirrorBlock_; // block number for mirror FAT
|
|
||||||
//
|
|
||||||
uint32_t allocSearchStart_; // start cluster for alloc search
|
|
||||||
uint8_t blocksPerCluster_; // cluster size in blocks
|
|
||||||
uint32_t blocksPerFat_; // FAT size in blocks
|
|
||||||
uint32_t clusterCount_; // clusters in one FAT
|
|
||||||
uint8_t clusterSizeShift_; // shift to convert cluster count to block count
|
|
||||||
uint32_t dataStartBlock_; // first data block number
|
|
||||||
uint8_t fatCount_; // number of FATs on volume
|
|
||||||
uint32_t fatStartBlock_; // start block for first FAT
|
|
||||||
uint8_t fatType_; // volume type (12, 16, OR 32)
|
|
||||||
uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir
|
|
||||||
uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
uint8_t allocContiguous(uint32_t count, uint32_t* curCluster);
|
|
||||||
uint8_t blockOfCluster(uint32_t position) const {
|
|
||||||
return (position >> 9) & (blocksPerCluster_ - 1);}
|
|
||||||
uint32_t clusterStartBlock(uint32_t cluster) const {
|
|
||||||
return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);}
|
|
||||||
uint32_t blockNumber(uint32_t cluster, uint32_t position) const {
|
|
||||||
return clusterStartBlock(cluster) + blockOfCluster(position);}
|
|
||||||
static uint8_t cacheFlush(void);
|
|
||||||
static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action);
|
|
||||||
static void cacheSetDirty(void) {cacheDirty_ |= CACHE_FOR_WRITE;}
|
|
||||||
static uint8_t cacheZeroBlock(uint32_t blockNumber);
|
|
||||||
uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const;
|
|
||||||
uint8_t fatGet(uint32_t cluster, uint32_t* value) const;
|
|
||||||
uint8_t fatPut(uint32_t cluster, uint32_t value);
|
|
||||||
uint8_t fatPutEOC(uint32_t cluster) {
|
|
||||||
return fatPut(cluster, 0x0FFFFFFF);
|
|
||||||
}
|
|
||||||
uint8_t freeChain(uint32_t cluster);
|
|
||||||
uint8_t isEOC(uint32_t cluster) const {
|
|
||||||
return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN);
|
|
||||||
}
|
|
||||||
uint8_t readBlock(uint32_t block, uint8_t* dst) {
|
|
||||||
return sdCard_->readBlock(block, dst);}
|
|
||||||
uint8_t readData(uint32_t block, uint16_t offset,
|
|
||||||
uint16_t count, uint8_t* dst) {
|
|
||||||
return sdCard_->readData(block, offset, count, dst);
|
|
||||||
}
|
|
||||||
uint8_t writeBlock(uint32_t block, const uint8_t* dst) {
|
|
||||||
return sdCard_->writeBlock(block, dst);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
#endif // SdFat_h
|
#endif // SdFat_h
|
||||||
|
|
108
Marlin/SdFatConfig.h
Normal file
108
Marlin/SdFatConfig.h
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/* Arduino SdFat Library
|
||||||
|
* Copyright (C) 2009 by William Greiman
|
||||||
|
*
|
||||||
|
* This file is part of the Arduino SdFat Library
|
||||||
|
*
|
||||||
|
* This Library is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This Library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with the Arduino SdFat Library. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief configuration definitions
|
||||||
|
*/
|
||||||
|
#ifndef SdFatConfig_h
|
||||||
|
#define SdFatConfig_h
|
||||||
|
#include <stdint.h>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* 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 1
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* 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 5 (F_CPU/64)
|
||||||
|
* or 6 (F_CPU/128).
|
||||||
|
*/
|
||||||
|
#define SPI_SD_INIT_RATE 5
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Set the SS pin high for hardware SPI. If SS is chip select for another SPI
|
||||||
|
* device this will disable that device during the SD init phase.
|
||||||
|
*/
|
||||||
|
#define SET_SPI_SS_HIGH 1
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Define MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos.
|
||||||
|
* Pins used are SS 10, MOSI 11, MISO 12, and SCK 13.
|
||||||
|
*
|
||||||
|
* MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used
|
||||||
|
* on Mega Arduinos. Software SPI works well with GPS Shield V1.1
|
||||||
|
* but many SD cards will fail with GPS Shield V1.0.
|
||||||
|
*/
|
||||||
|
#define MEGA_SOFT_SPI 0
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Set USE_SOFTWARE_SPI nonzero to always use software SPI.
|
||||||
|
*/
|
||||||
|
#define USE_SOFTWARE_SPI 0
|
||||||
|
// define software SPI pins so Mega can use unmodified 168/328 shields
|
||||||
|
/** Software SPI chip select pin for the SD */
|
||||||
|
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;
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* The __cxa_pure_virtual function is an error handler that is invoked when
|
||||||
|
* a pure virtual function is called.
|
||||||
|
*/
|
||||||
|
#define USE_CXA_PURE_VIRTUAL 1
|
||||||
|
#endif // SdFatConfig_h
|
|
@ -17,11 +17,11 @@
|
||||||
* along with the Arduino SdFat Library. If not, see
|
* along with the Arduino SdFat Library. If not, see
|
||||||
* <http://www.gnu.org/licenses/>.
|
* <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifndef FatStructs_h
|
#ifndef SdFatStructs_h
|
||||||
#define FatStructs_h
|
#define SdFatStructs_h
|
||||||
/**
|
/**
|
||||||
* \file
|
* \file
|
||||||
* FAT file structures
|
* \brief FAT file structures
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* mostly from Microsoft document fatgen103.doc
|
* mostly from Microsoft document fatgen103.doc
|
||||||
|
@ -32,6 +32,8 @@
|
||||||
uint8_t const BOOTSIG0 = 0X55;
|
uint8_t const BOOTSIG0 = 0X55;
|
||||||
/** Value for byte 511 of boot block or MBR */
|
/** Value for byte 511 of boot block or MBR */
|
||||||
uint8_t const BOOTSIG1 = 0XAA;
|
uint8_t const BOOTSIG1 = 0XAA;
|
||||||
|
/** Value for bootSignature field int FAT/FAT32 boot sector */
|
||||||
|
uint8_t const EXTENDED_BOOT_SIG = 0X29;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* \struct partitionTable
|
* \struct partitionTable
|
||||||
|
@ -104,7 +106,7 @@ typedef struct partitionTable part_t;
|
||||||
struct masterBootRecord {
|
struct masterBootRecord {
|
||||||
/** Code Area for master boot program. */
|
/** Code Area for master boot program. */
|
||||||
uint8_t codeArea[440];
|
uint8_t codeArea[440];
|
||||||
/** Optional WindowsNT disk signature. May contain more boot code. */
|
/** Optional Windows NT disk signature. May contain boot code. */
|
||||||
uint32_t diskSignature;
|
uint32_t diskSignature;
|
||||||
/** Usually zero but may be more boot code. */
|
/** Usually zero but may be more boot code. */
|
||||||
uint16_t usuallyZero;
|
uint16_t usuallyZero;
|
||||||
|
@ -119,47 +121,59 @@ struct masterBootRecord {
|
||||||
typedef struct masterBootRecord mbr_t;
|
typedef struct masterBootRecord mbr_t;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* \struct biosParmBlock
|
* \struct fat_boot
|
||||||
*
|
*
|
||||||
* \brief BIOS parameter block
|
* \brief Boot sector for a FAT12/FAT16 volume.
|
||||||
*
|
*
|
||||||
* The BIOS parameter block describes the physical layout of a FAT volume.
|
|
||||||
*/
|
*/
|
||||||
struct biosParmBlock {
|
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];
|
||||||
/**
|
/**
|
||||||
* Count of bytes per sector. This value may take on only the
|
* The size of a hardware sector. Valid decimal values for this
|
||||||
* following values: 512, 1024, 2048 or 4096
|
* 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;
|
uint16_t bytesPerSector;
|
||||||
/**
|
/**
|
||||||
* Number of sectors per allocation unit. This value must be a
|
* Number of sectors per allocation unit. This value must be a
|
||||||
* power of 2 that is greater than 0. The legal values are
|
* power of 2 that is greater than 0. The legal values are
|
||||||
* 1, 2, 4, 8, 16, 32, 64, and 128.
|
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
|
||||||
*/
|
*/
|
||||||
uint8_t sectorsPerCluster;
|
uint8_t sectorsPerCluster;
|
||||||
/**
|
/**
|
||||||
* Number of sectors before the first FAT.
|
* The number of sectors preceding the start of the first FAT,
|
||||||
* This value must not be zero.
|
* including the boot sector. The value of this field is always 1.
|
||||||
*/
|
*/
|
||||||
uint16_t reservedSectorCount;
|
uint16_t reservedSectorCount;
|
||||||
/** The count of FAT data structures on the volume. This field should
|
/**
|
||||||
* always contain the value 2 for any FAT volume of any type.
|
* The number of copies of the FAT on the volume.
|
||||||
|
* The value of this field is always 2.
|
||||||
*/
|
*/
|
||||||
uint8_t fatCount;
|
uint8_t fatCount;
|
||||||
/**
|
/**
|
||||||
* For FAT12 and FAT16 volumes, this field contains the count of
|
* For FAT12 and FAT16 volumes, this field contains the count of
|
||||||
* 32-byte directory entries in the root directory. For FAT32 volumes,
|
* 32-byte directory entries in the root directory. For FAT32 volumes,
|
||||||
* this field must be set to 0. For FAT12 and FAT16 volumes, this
|
* this field must be set to 0. For FAT12 and FAT16 volumes, this
|
||||||
* value should always specify a count that when multiplied by 32
|
* value should always specify a count that when multiplied by 32
|
||||||
* results in a multiple of bytesPerSector. FAT16 volumes should
|
* results in a multiple of bytesPerSector. FAT16 volumes should
|
||||||
* use the value 512.
|
* use the value 512.
|
||||||
*/
|
*/
|
||||||
uint16_t rootDirEntryCount;
|
uint16_t rootDirEntryCount;
|
||||||
/**
|
/**
|
||||||
* This field is the old 16-bit total count of sectors on the volume.
|
* 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
|
* 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
|
* of the volume. This field can be 0; if it is 0, then totalSectors32
|
||||||
* must be non-zero. For FAT32 volumes, this field must be 0. For
|
* must be nonzero. For FAT32 volumes, this field must be 0. For
|
||||||
* FAT12 and FAT16 volumes, this field contains the sector count, and
|
* FAT12 and FAT16 volumes, this field contains the sector count, and
|
||||||
* totalSectors32 is 0 if the total sector count fits
|
* totalSectors32 is 0 if the total sector count fits
|
||||||
* (is less than 0x10000).
|
* (is less than 0x10000).
|
||||||
|
@ -168,7 +182,7 @@ struct biosParmBlock {
|
||||||
/**
|
/**
|
||||||
* This dates back to the old MS-DOS 1.x media determination and is
|
* 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
|
* no longer usually used for anything. 0xF8 is the standard value
|
||||||
* for fixed (non-removable) media. For removable media, 0xF0 is
|
* for fixed (nonremovable) media. For removable media, 0xF0 is
|
||||||
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
|
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
|
||||||
*/
|
*/
|
||||||
uint8_t mediaType;
|
uint8_t mediaType;
|
||||||
|
@ -179,23 +193,136 @@ struct biosParmBlock {
|
||||||
*/
|
*/
|
||||||
uint16_t sectorsPerFat16;
|
uint16_t sectorsPerFat16;
|
||||||
/** Sectors per track for interrupt 0x13. Not used otherwise. */
|
/** Sectors per track for interrupt 0x13. Not used otherwise. */
|
||||||
uint16_t sectorsPerTrtack;
|
uint16_t sectorsPerTrack;
|
||||||
/** Number of heads for interrupt 0x13. Not used otherwise. */
|
/** Number of heads for interrupt 0x13. Not used otherwise. */
|
||||||
uint16_t headCount;
|
uint16_t headCount;
|
||||||
/**
|
/**
|
||||||
* Count of hidden sectors preceding the partition that contains this
|
* Count of hidden sectors preceding the partition that contains this
|
||||||
* FAT volume. This field is generally only relevant for media
|
* FAT volume. This field is generally only relevant for media
|
||||||
* visible on interrupt 0x13.
|
* visible on interrupt 0x13.
|
||||||
*/
|
*/
|
||||||
uint32_t hidddenSectors;
|
uint32_t hidddenSectors;
|
||||||
/**
|
/**
|
||||||
* This field is the new 32-bit total count of sectors on the volume.
|
* 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
|
* 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
|
* of the volume. This field can be 0; if it is 0, then
|
||||||
* totalSectors16 must be non-zero.
|
* totalSectors16 must be nonzero.
|
||||||
*/
|
*/
|
||||||
uint32_t totalSectors32;
|
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;
|
||||||
|
};
|
||||||
|
/** 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.
|
* Count of sectors occupied by one FAT on FAT32 volumes.
|
||||||
*/
|
*/
|
||||||
uint32_t sectorsPerFat32;
|
uint32_t sectorsPerFat32;
|
||||||
|
@ -206,7 +333,8 @@ struct biosParmBlock {
|
||||||
* Only valid if mirroring is disabled.
|
* Only valid if mirroring is disabled.
|
||||||
* Bits 4-6 -- Reserved.
|
* Bits 4-6 -- Reserved.
|
||||||
* Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
|
* 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.
|
* -- 1 means only one FAT is active; it is the one referenced
|
||||||
|
* in bits 0-3.
|
||||||
* Bits 8-15 -- Reserved.
|
* Bits 8-15 -- Reserved.
|
||||||
*/
|
*/
|
||||||
uint16_t fat32Flags;
|
uint16_t fat32Flags;
|
||||||
|
@ -226,7 +354,7 @@ struct biosParmBlock {
|
||||||
*/
|
*/
|
||||||
uint16_t fat32FSInfo;
|
uint16_t fat32FSInfo;
|
||||||
/**
|
/**
|
||||||
* If non-zero, indicates the sector number in the reserved area
|
* If nonzero, indicates the sector number in the reserved area
|
||||||
* of the volume of a copy of the boot record. Usually 6.
|
* of the volume of a copy of the boot record. Usually 6.
|
||||||
* No value other than 6 is recommended.
|
* No value other than 6 is recommended.
|
||||||
*/
|
*/
|
||||||
|
@ -236,34 +364,33 @@ struct biosParmBlock {
|
||||||
* should always set all of the bytes of this field to 0.
|
* should always set all of the bytes of this field to 0.
|
||||||
*/
|
*/
|
||||||
uint8_t fat32Reserved[12];
|
uint8_t fat32Reserved[12];
|
||||||
};
|
/**
|
||||||
/** Type name for biosParmBlock */
|
* Related to the BIOS physical drive number. Floppy drives are
|
||||||
typedef struct biosParmBlock bpb_t;
|
* 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
|
||||||
* \struct fat32BootSector
|
* call to specify the device to access. The value is only
|
||||||
*
|
* relevant if the device is a boot device.
|
||||||
* \brief Boot sector for a FAT16 or FAT32 volume.
|
*/
|
||||||
*
|
|
||||||
*/
|
|
||||||
struct fat32BootSector {
|
|
||||||
/** X86 jmp to boot program */
|
|
||||||
uint8_t jmpToBootCode[3];
|
|
||||||
/** informational only - don't depend on it */
|
|
||||||
char oemName[8];
|
|
||||||
/** BIOS Parameter Block */
|
|
||||||
bpb_t bpb;
|
|
||||||
/** for int0x13 use value 0X80 for hard drive */
|
|
||||||
uint8_t driveNumber;
|
uint8_t driveNumber;
|
||||||
/** used by Windows NT - should be zero for FAT */
|
/** used by Windows NT - should be zero for FAT */
|
||||||
uint8_t reserved1;
|
uint8_t reserved1;
|
||||||
/** 0X29 if next three fields are valid */
|
/** 0X29 if next three fields are valid */
|
||||||
uint8_t bootSignature;
|
uint8_t bootSignature;
|
||||||
/** usually generated by combining date and time */
|
/**
|
||||||
|
* 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;
|
uint32_t volumeSerialNumber;
|
||||||
/** should match volume label in root dir */
|
/**
|
||||||
|
* 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];
|
char volumeLabel[11];
|
||||||
/** informational only - don't depend on it */
|
/**
|
||||||
|
* A text field with a value of FAT32.
|
||||||
|
*/
|
||||||
char fileSystemType[8];
|
char fileSystemType[8];
|
||||||
/** X86 boot code */
|
/** X86 boot code */
|
||||||
uint8_t bootCode[420];
|
uint8_t bootCode[420];
|
||||||
|
@ -272,8 +399,54 @@ struct fat32BootSector {
|
||||||
/** must be 0XAA */
|
/** must be 0XAA */
|
||||||
uint8_t bootSectorSig1;
|
uint8_t bootSectorSig1;
|
||||||
};
|
};
|
||||||
|
/** 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];
|
||||||
|
};
|
||||||
|
/** Type name for FAT32 FSINFO Sector */
|
||||||
|
typedef struct fat32_fsinfo fat32_fsinfo_t;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// End Of Chain values for FAT entries
|
// 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. */
|
/** FAT16 end of chain value used by Microsoft. */
|
||||||
uint16_t const FAT16EOC = 0XFFFF;
|
uint16_t const FAT16EOC = 0XFFFF;
|
||||||
/** Minimum value for FAT16 EOC. Use to test for EOC. */
|
/** Minimum value for FAT16 EOC. Use to test for EOC. */
|
||||||
|
@ -284,9 +457,6 @@ uint32_t const FAT32EOC = 0X0FFFFFFF;
|
||||||
uint32_t const FAT32EOC_MIN = 0X0FFFFFF8;
|
uint32_t const FAT32EOC_MIN = 0X0FFFFFF8;
|
||||||
/** Mask a for FAT32 entry. Entries are 28 bits. */
|
/** Mask a for FAT32 entry. Entries are 28 bits. */
|
||||||
uint32_t const FAT32MASK = 0X0FFFFFFF;
|
uint32_t const FAT32MASK = 0X0FFFFFFF;
|
||||||
|
|
||||||
/** Type name for fat32BootSector */
|
|
||||||
typedef struct fat32BootSector fbs_t;
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* \struct directoryEntry
|
* \struct directoryEntry
|
||||||
|
@ -319,8 +489,8 @@ typedef struct fat32BootSector fbs_t;
|
||||||
* The valid time range is from Midnight 00:00:00 to 23:59:58.
|
* The valid time range is from Midnight 00:00:00 to 23:59:58.
|
||||||
*/
|
*/
|
||||||
struct directoryEntry {
|
struct directoryEntry {
|
||||||
/**
|
/** Short 8.3 name.
|
||||||
* Short 8.3 name.
|
*
|
||||||
* The first eight bytes contain the file name with blank fill.
|
* The first eight bytes contain the file name with blank fill.
|
||||||
* The last three bytes contain the file extension with blank fill.
|
* The last three bytes contain the file extension with blank fill.
|
||||||
*/
|
*/
|
||||||
|
@ -397,22 +567,38 @@ uint8_t const DIR_ATT_LONG_NAME = 0X0F;
|
||||||
uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F;
|
uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F;
|
||||||
/** defined attribute bits */
|
/** defined attribute bits */
|
||||||
uint8_t const DIR_ATT_DEFINED_BITS = 0X3F;
|
uint8_t const DIR_ATT_DEFINED_BITS = 0X3F;
|
||||||
/** Directory entry is part of a long name */
|
/** 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) {
|
static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) {
|
||||||
return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
|
return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
|
||||||
}
|
}
|
||||||
/** Mask for file/subdirectory tests */
|
/** Mask for file/subdirectory tests */
|
||||||
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
|
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
|
||||||
/** Directory entry is for a file */
|
/** 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) {
|
static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
|
||||||
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
|
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
|
||||||
}
|
}
|
||||||
/** Directory entry is for a subdirectory */
|
/** 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) {
|
static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
|
||||||
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
|
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
|
||||||
}
|
}
|
||||||
/** Directory entry is for a file or subdirectory */
|
/** 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) {
|
static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) {
|
||||||
return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
|
return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
|
||||||
}
|
}
|
||||||
#endif // FatStructs_h
|
#endif // SdFatStructs_h
|
74
Marlin/SdFatUtil.cpp
Normal file
74
Marlin/SdFatUtil.cpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/* Arduino SdFat Library
|
||||||
|
* Copyright (C) 2008 by William Greiman
|
||||||
|
*
|
||||||
|
* This file is part of the Arduino SdFat Library
|
||||||
|
*
|
||||||
|
* This Library is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This Library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with the Arduino SdFat Library. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "SdFatUtil.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** Amount of free RAM
|
||||||
|
* \return The number of free bytes.
|
||||||
|
*/
|
||||||
|
int SdFatUtil::FreeRam() {
|
||||||
|
extern int __bss_end;
|
||||||
|
extern int* __brkval;
|
||||||
|
int free_memory;
|
||||||
|
if (reinterpret_cast<int>(__brkval) == 0) {
|
||||||
|
// if no heap use from end of bss section
|
||||||
|
free_memory = reinterpret_cast<int>(&free_memory)
|
||||||
|
- reinterpret_cast<int>(&__bss_end);
|
||||||
|
} else {
|
||||||
|
// use from top of stack to heap
|
||||||
|
free_memory = reinterpret_cast<int>(&free_memory)
|
||||||
|
- reinterpret_cast<int>(__brkval);
|
||||||
|
}
|
||||||
|
return free_memory;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** %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(&Serial, str);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** %Print a string in flash memory to Serial followed by a CR/LF.
|
||||||
|
*
|
||||||
|
* \param[in] str Pointer to string stored in flash memory.
|
||||||
|
*/
|
||||||
|
void SdFatUtil::SerialPrintln_P(PGM_P str) {
|
||||||
|
println_P(&Serial, str);
|
||||||
|
}
|
|
@ -21,50 +21,26 @@
|
||||||
#define SdFatUtil_h
|
#define SdFatUtil_h
|
||||||
/**
|
/**
|
||||||
* \file
|
* \file
|
||||||
* Useful utility functions.
|
* \brief Useful utility functions.
|
||||||
*/
|
*/
|
||||||
#include <WProgram.h>
|
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
|
#if ARDUINO < 100
|
||||||
|
#include <WProgram.h>
|
||||||
|
#else // ARDUINO
|
||||||
|
#include <Arduino.h>
|
||||||
|
#endif // ARDUINO
|
||||||
/** Store and print a string in flash memory.*/
|
/** Store and print a string in flash memory.*/
|
||||||
#define PgmPrint(x) SerialPrint_P(PSTR(x))
|
#define PgmPrint(x) SerialPrint_P(PSTR(x))
|
||||||
/** Store and print a string in flash memory followed by a CR/LF.*/
|
/** Store and print a string in flash memory followed by a CR/LF.*/
|
||||||
#define PgmPrintln(x) SerialPrintln_P(PSTR(x))
|
#define PgmPrintln(x) SerialPrintln_P(PSTR(x))
|
||||||
/** Defined so doxygen works for function definitions. */
|
|
||||||
#define NOINLINE __attribute__((noinline))
|
namespace SdFatUtil {
|
||||||
//------------------------------------------------------------------------------
|
int FreeRam();
|
||||||
/** Return the number of bytes currently free in RAM. */
|
void print_P(Print* pr, PGM_P str);
|
||||||
static int FreeRam(void) {
|
void println_P(Print* pr, PGM_P str);
|
||||||
extern int __bss_end;
|
void SerialPrint_P(PGM_P str);
|
||||||
extern int* __brkval;
|
void SerialPrintln_P(PGM_P str);
|
||||||
int free_memory;
|
|
||||||
if (reinterpret_cast<int>(__brkval) == 0) {
|
|
||||||
// if no heap use from end of bss section
|
|
||||||
free_memory = reinterpret_cast<int>(&free_memory)
|
|
||||||
- reinterpret_cast<int>(&__bss_end);
|
|
||||||
} else {
|
|
||||||
// use from top of stack to heap
|
|
||||||
free_memory = reinterpret_cast<int>(&free_memory)
|
|
||||||
- reinterpret_cast<int>(__brkval);
|
|
||||||
}
|
|
||||||
return free_memory;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* %Print a string in flash memory to the serial port.
|
|
||||||
*
|
|
||||||
* \param[in] str Pointer to string stored in flash memory.
|
|
||||||
*/
|
|
||||||
static NOINLINE void SerialPrint_P(PGM_P str) {
|
|
||||||
for (uint8_t c; (c = pgm_read_byte(str)); str++) Serial.print(c);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* %Print a string in flash memory followed by a CR/LF.
|
|
||||||
*
|
|
||||||
* \param[in] str Pointer to string stored in flash memory.
|
|
||||||
*/
|
|
||||||
static NOINLINE void SerialPrintln_P(PGM_P str) {
|
|
||||||
SerialPrint_P(str);
|
|
||||||
Serial.println();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using namespace SdFatUtil; // NOLINT
|
||||||
#endif // #define SdFatUtil_h
|
#endif // #define SdFatUtil_h
|
||||||
|
|
|
@ -1,202 +0,0 @@
|
||||||
/* Arduino SdFat Library
|
|
||||||
* Copyright (C) 2009 by William Greiman
|
|
||||||
*
|
|
||||||
* This file is part of the Arduino SdFat Library
|
|
||||||
*
|
|
||||||
* This Library is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This Library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with the Arduino SdFat Library. If not, see
|
|
||||||
* <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
\mainpage Arduino SdFat Library
|
|
||||||
<CENTER>Copyright © 2009 by William Greiman
|
|
||||||
</CENTER>
|
|
||||||
|
|
||||||
\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.
|
|
||||||
|
|
||||||
The SdFat only supports short 8.3 names.
|
|
||||||
|
|
||||||
The main classes in SdFat are Sd2Card, SdVolume, and SdFile.
|
|
||||||
|
|
||||||
The Sd2Card class supports access to standard SD cards and SDHC cards. Most
|
|
||||||
applications will only need to call the Sd2Card::init() member function.
|
|
||||||
|
|
||||||
The SdVolume class supports FAT16 and FAT32 partitions. Most applications
|
|
||||||
will only need to call the SdVolume::init() member function.
|
|
||||||
|
|
||||||
The SdFile class provides file access functions such as open(), read(),
|
|
||||||
remove(), write(), close() and sync(). This class supports access to the root
|
|
||||||
directory and subdirectories.
|
|
||||||
|
|
||||||
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
|
|
||||||
<A HREF = "http://www.adafruit.com/"> Adafruit Industries</A>
|
|
||||||
<A HREF = "http://www.ladyada.net/make/waveshield/"> Wave Shield</A>.
|
|
||||||
|
|
||||||
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 \link Print::print() print()\endlink,
|
|
||||||
\link Print::println() println() \endlink
|
|
||||||
or \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink
|
|
||||||
at the appropriate time to force data and directory information to be written
|
|
||||||
to the SD Card. Data and directory information are also written to the SD card
|
|
||||||
when \link SdFile::close() close() \endlink is called.
|
|
||||||
|
|
||||||
\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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
The best way to restore an SD card's format 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.
|
|
||||||
|
|
||||||
\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
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
1215
Marlin/SdFile.cpp
1215
Marlin/SdFile.cpp
File diff suppressed because it is too large
Load diff
42
Marlin/SdFile.h
Normal file
42
Marlin/SdFile.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/* Arduino SdFat Library
|
||||||
|
* Copyright (C) 2009 by William Greiman
|
||||||
|
*
|
||||||
|
* This file is part of the Arduino SdFat Library
|
||||||
|
*
|
||||||
|
* This Library is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This Library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with the Arduino SdFat Library. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief SdFile class
|
||||||
|
*/
|
||||||
|
#include "SdBaseFile.h"
|
||||||
|
#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);
|
||||||
|
void write(uint8_t b);
|
||||||
|
int16_t write(const void* buf, uint16_t nbyte);
|
||||||
|
void write(const char* str);
|
||||||
|
void write_P(PGM_P str);
|
||||||
|
void writeln_P(PGM_P str);
|
||||||
|
};
|
||||||
|
#endif // SdFile_h
|
237
Marlin/SdInfo.h
237
Marlin/SdInfo.h
|
@ -26,10 +26,10 @@
|
||||||
// Part 1
|
// Part 1
|
||||||
// Physical Layer
|
// Physical Layer
|
||||||
// Simplified Specification
|
// Simplified Specification
|
||||||
// Version 2.00
|
// Version 3.01
|
||||||
// September 25, 2006
|
// May 18, 2010
|
||||||
//
|
//
|
||||||
// www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
|
// http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// SD card commands
|
// SD card commands
|
||||||
/** GO_IDLE_STATE - init card in spi mode if CS low */
|
/** GO_IDLE_STATE - init card in spi mode if CS low */
|
||||||
|
@ -40,10 +40,14 @@ uint8_t const CMD8 = 0X08;
|
||||||
uint8_t const CMD9 = 0X09;
|
uint8_t const CMD9 = 0X09;
|
||||||
/** SEND_CID - read the card identification information (CID register) */
|
/** SEND_CID - read the card identification information (CID register) */
|
||||||
uint8_t const CMD10 = 0X0A;
|
uint8_t const CMD10 = 0X0A;
|
||||||
|
/** STOP_TRANSMISSION - end multiple block read sequence */
|
||||||
|
uint8_t const CMD12 = 0X0C;
|
||||||
/** SEND_STATUS - read the card status register */
|
/** SEND_STATUS - read the card status register */
|
||||||
uint8_t const CMD13 = 0X0D;
|
uint8_t const CMD13 = 0X0D;
|
||||||
/** READ_BLOCK - read a single data block from the card */
|
/** READ_SINGLE_BLOCK - read a single data block from the card */
|
||||||
uint8_t const CMD17 = 0X11;
|
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 */
|
/** WRITE_BLOCK - write a single data block to the card */
|
||||||
uint8_t const CMD24 = 0X18;
|
uint8_t const CMD24 = 0X18;
|
||||||
/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */
|
/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */
|
||||||
|
@ -83,148 +87,187 @@ uint8_t const DATA_RES_MASK = 0X1F;
|
||||||
/** write data accepted token */
|
/** write data accepted token */
|
||||||
uint8_t const DATA_RES_ACCEPTED = 0X05;
|
uint8_t const DATA_RES_ACCEPTED = 0X05;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
/** Card IDentification (CID) register */
|
||||||
typedef struct CID {
|
typedef struct CID {
|
||||||
// byte 0
|
// byte 0
|
||||||
uint8_t mid; // Manufacturer ID
|
/** Manufacturer ID */
|
||||||
|
unsigned char mid;
|
||||||
// byte 1-2
|
// byte 1-2
|
||||||
char oid[2]; // OEM/Application ID
|
/** OEM/Application ID */
|
||||||
|
char oid[2];
|
||||||
// byte 3-7
|
// byte 3-7
|
||||||
char pnm[5]; // Product name
|
/** Product name */
|
||||||
|
char pnm[5];
|
||||||
// byte 8
|
// byte 8
|
||||||
unsigned prv_m : 4; // Product revision n.m
|
/** Product revision least significant digit */
|
||||||
unsigned prv_n : 4;
|
unsigned char prv_m : 4;
|
||||||
|
/** Product revision most significant digit */
|
||||||
|
unsigned char prv_n : 4;
|
||||||
// byte 9-12
|
// byte 9-12
|
||||||
uint32_t psn; // Product serial number
|
/** Product serial number */
|
||||||
|
uint32_t psn;
|
||||||
// byte 13
|
// byte 13
|
||||||
unsigned mdt_year_high : 4; // Manufacturing date
|
/** Manufacturing date year low digit */
|
||||||
unsigned reserved : 4;
|
unsigned char mdt_year_high : 4;
|
||||||
|
/** not used */
|
||||||
|
unsigned char reserved : 4;
|
||||||
// byte 14
|
// byte 14
|
||||||
unsigned mdt_month : 4;
|
/** Manufacturing date month */
|
||||||
unsigned mdt_year_low :4;
|
unsigned char mdt_month : 4;
|
||||||
|
/** Manufacturing date year low digit */
|
||||||
|
unsigned char mdt_year_low :4;
|
||||||
// byte 15
|
// byte 15
|
||||||
unsigned always1 : 1;
|
/** not used always 1 */
|
||||||
unsigned crc : 7;
|
unsigned char always1 : 1;
|
||||||
|
/** CRC7 checksum */
|
||||||
|
unsigned char crc : 7;
|
||||||
}cid_t;
|
}cid_t;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// CSD for version 1.00 cards
|
/** CSD for version 1.00 cards */
|
||||||
typedef struct CSDV1 {
|
typedef struct CSDV1 {
|
||||||
// byte 0
|
// byte 0
|
||||||
unsigned reserved1 : 6;
|
unsigned char reserved1 : 6;
|
||||||
unsigned csd_ver : 2;
|
unsigned char csd_ver : 2;
|
||||||
// byte 1
|
// byte 1
|
||||||
uint8_t taac;
|
unsigned char taac;
|
||||||
// byte 2
|
// byte 2
|
||||||
uint8_t nsac;
|
unsigned char nsac;
|
||||||
// byte 3
|
// byte 3
|
||||||
uint8_t tran_speed;
|
unsigned char tran_speed;
|
||||||
// byte 4
|
// byte 4
|
||||||
uint8_t ccc_high;
|
unsigned char ccc_high;
|
||||||
// byte 5
|
// byte 5
|
||||||
unsigned read_bl_len : 4;
|
unsigned char read_bl_len : 4;
|
||||||
unsigned ccc_low : 4;
|
unsigned char ccc_low : 4;
|
||||||
// byte 6
|
// byte 6
|
||||||
unsigned c_size_high : 2;
|
unsigned char c_size_high : 2;
|
||||||
unsigned reserved2 : 2;
|
unsigned char reserved2 : 2;
|
||||||
unsigned dsr_imp : 1;
|
unsigned char dsr_imp : 1;
|
||||||
unsigned read_blk_misalign :1;
|
unsigned char read_blk_misalign :1;
|
||||||
unsigned write_blk_misalign : 1;
|
unsigned char write_blk_misalign : 1;
|
||||||
unsigned read_bl_partial : 1;
|
unsigned char read_bl_partial : 1;
|
||||||
// byte 7
|
// byte 7
|
||||||
uint8_t c_size_mid;
|
unsigned char c_size_mid;
|
||||||
// byte 8
|
// byte 8
|
||||||
unsigned vdd_r_curr_max : 3;
|
unsigned char vdd_r_curr_max : 3;
|
||||||
unsigned vdd_r_curr_min : 3;
|
unsigned char vdd_r_curr_min : 3;
|
||||||
unsigned c_size_low :2;
|
unsigned char c_size_low :2;
|
||||||
// byte 9
|
// byte 9
|
||||||
unsigned c_size_mult_high : 2;
|
unsigned char c_size_mult_high : 2;
|
||||||
unsigned vdd_w_cur_max : 3;
|
unsigned char vdd_w_cur_max : 3;
|
||||||
unsigned vdd_w_curr_min : 3;
|
unsigned char vdd_w_curr_min : 3;
|
||||||
// byte 10
|
// byte 10
|
||||||
unsigned sector_size_high : 6;
|
unsigned char sector_size_high : 6;
|
||||||
unsigned erase_blk_en : 1;
|
unsigned char erase_blk_en : 1;
|
||||||
unsigned c_size_mult_low : 1;
|
unsigned char c_size_mult_low : 1;
|
||||||
// byte 11
|
// byte 11
|
||||||
unsigned wp_grp_size : 7;
|
unsigned char wp_grp_size : 7;
|
||||||
unsigned sector_size_low : 1;
|
unsigned char sector_size_low : 1;
|
||||||
// byte 12
|
// byte 12
|
||||||
unsigned write_bl_len_high : 2;
|
unsigned char write_bl_len_high : 2;
|
||||||
unsigned r2w_factor : 3;
|
unsigned char r2w_factor : 3;
|
||||||
unsigned reserved3 : 2;
|
unsigned char reserved3 : 2;
|
||||||
unsigned wp_grp_enable : 1;
|
unsigned char wp_grp_enable : 1;
|
||||||
// byte 13
|
// byte 13
|
||||||
unsigned reserved4 : 5;
|
unsigned char reserved4 : 5;
|
||||||
unsigned write_partial : 1;
|
unsigned char write_partial : 1;
|
||||||
unsigned write_bl_len_low : 2;
|
unsigned char write_bl_len_low : 2;
|
||||||
// byte 14
|
// byte 14
|
||||||
unsigned reserved5: 2;
|
unsigned char reserved5: 2;
|
||||||
unsigned file_format : 2;
|
unsigned char file_format : 2;
|
||||||
unsigned tmp_write_protect : 1;
|
unsigned char tmp_write_protect : 1;
|
||||||
unsigned perm_write_protect : 1;
|
unsigned char perm_write_protect : 1;
|
||||||
unsigned copy : 1;
|
unsigned char copy : 1;
|
||||||
unsigned file_format_grp : 1;
|
/** Indicates the file format on the card */
|
||||||
|
unsigned char file_format_grp : 1;
|
||||||
// byte 15
|
// byte 15
|
||||||
unsigned always1 : 1;
|
unsigned char always1 : 1;
|
||||||
unsigned crc : 7;
|
unsigned char crc : 7;
|
||||||
}csd1_t;
|
}csd1_t;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// CSD for version 2.00 cards
|
/** CSD for version 2.00 cards */
|
||||||
typedef struct CSDV2 {
|
typedef struct CSDV2 {
|
||||||
// byte 0
|
// byte 0
|
||||||
unsigned reserved1 : 6;
|
unsigned char reserved1 : 6;
|
||||||
unsigned csd_ver : 2;
|
unsigned char csd_ver : 2;
|
||||||
// byte 1
|
// byte 1
|
||||||
uint8_t taac;
|
/** fixed to 0X0E */
|
||||||
|
unsigned char taac;
|
||||||
// byte 2
|
// byte 2
|
||||||
uint8_t nsac;
|
/** fixed to 0 */
|
||||||
|
unsigned char nsac;
|
||||||
// byte 3
|
// byte 3
|
||||||
uint8_t tran_speed;
|
unsigned char tran_speed;
|
||||||
// byte 4
|
// byte 4
|
||||||
uint8_t ccc_high;
|
unsigned char ccc_high;
|
||||||
// byte 5
|
// byte 5
|
||||||
unsigned read_bl_len : 4;
|
/** This field is fixed to 9h, which indicates READ_BL_LEN=512 Byte */
|
||||||
unsigned ccc_low : 4;
|
unsigned char read_bl_len : 4;
|
||||||
|
unsigned char ccc_low : 4;
|
||||||
// byte 6
|
// byte 6
|
||||||
unsigned reserved2 : 4;
|
/** not used */
|
||||||
unsigned dsr_imp : 1;
|
unsigned char reserved2 : 4;
|
||||||
unsigned read_blk_misalign :1;
|
unsigned char dsr_imp : 1;
|
||||||
unsigned write_blk_misalign : 1;
|
/** fixed to 0 */
|
||||||
unsigned read_bl_partial : 1;
|
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
|
// byte 7
|
||||||
unsigned reserved3 : 2;
|
/** not used */
|
||||||
unsigned c_size_high : 6;
|
unsigned char reserved3 : 2;
|
||||||
|
/** high part of card size */
|
||||||
|
unsigned char c_size_high : 6;
|
||||||
// byte 8
|
// byte 8
|
||||||
uint8_t c_size_mid;
|
/** middle part of card size */
|
||||||
|
unsigned char c_size_mid;
|
||||||
// byte 9
|
// byte 9
|
||||||
uint8_t c_size_low;
|
/** low part of card size */
|
||||||
|
unsigned char c_size_low;
|
||||||
// byte 10
|
// byte 10
|
||||||
unsigned sector_size_high : 6;
|
/** sector size is fixed at 64 KB */
|
||||||
unsigned erase_blk_en : 1;
|
unsigned char sector_size_high : 6;
|
||||||
unsigned reserved4 : 1;
|
/** fixed to 1 - erase single is supported */
|
||||||
|
unsigned char erase_blk_en : 1;
|
||||||
|
/** not used */
|
||||||
|
unsigned char reserved4 : 1;
|
||||||
// byte 11
|
// byte 11
|
||||||
unsigned wp_grp_size : 7;
|
unsigned char wp_grp_size : 7;
|
||||||
unsigned sector_size_low : 1;
|
/** sector size is fixed at 64 KB */
|
||||||
|
unsigned char sector_size_low : 1;
|
||||||
// byte 12
|
// byte 12
|
||||||
unsigned write_bl_len_high : 2;
|
/** write_bl_len fixed for 512 byte blocks */
|
||||||
unsigned r2w_factor : 3;
|
unsigned char write_bl_len_high : 2;
|
||||||
unsigned reserved5 : 2;
|
/** fixed value of 2 */
|
||||||
unsigned wp_grp_enable : 1;
|
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
|
// byte 13
|
||||||
unsigned reserved6 : 5;
|
unsigned char reserved6 : 5;
|
||||||
unsigned write_partial : 1;
|
/** always zero - no partial block read*/
|
||||||
unsigned write_bl_len_low : 2;
|
unsigned char write_partial : 1;
|
||||||
|
/** write_bl_len fixed for 512 byte blocks */
|
||||||
|
unsigned char write_bl_len_low : 2;
|
||||||
// byte 14
|
// byte 14
|
||||||
unsigned reserved7: 2;
|
unsigned char reserved7: 2;
|
||||||
unsigned file_format : 2;
|
/** Do not use always 0 */
|
||||||
unsigned tmp_write_protect : 1;
|
unsigned char file_format : 2;
|
||||||
unsigned perm_write_protect : 1;
|
unsigned char tmp_write_protect : 1;
|
||||||
unsigned copy : 1;
|
unsigned char perm_write_protect : 1;
|
||||||
unsigned file_format_grp : 1;
|
unsigned char copy : 1;
|
||||||
|
/** Do not use always 0 */
|
||||||
|
unsigned char file_format_grp : 1;
|
||||||
// byte 15
|
// byte 15
|
||||||
unsigned always1 : 1;
|
/** not used always 1 */
|
||||||
unsigned crc : 7;
|
unsigned char always1 : 1;
|
||||||
|
/** checksum */
|
||||||
|
unsigned char crc : 7;
|
||||||
}csd2_t;
|
}csd2_t;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// union of old and new style CSD register
|
/** union of old and new style CSD register */
|
||||||
union csd_t {
|
union csd_t {
|
||||||
csd1_t v1;
|
csd1_t v1;
|
||||||
csd2_t v2;
|
csd2_t v2;
|
||||||
|
|
|
@ -17,23 +17,28 @@
|
||||||
* along with the Arduino SdFat Library. If not, see
|
* along with the Arduino SdFat Library. If not, see
|
||||||
* <http://www.gnu.org/licenses/>.
|
* <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "SdFat.h"
|
#include "SdVolume.h"
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
#if !USE_MULTIPLE_CARDS
|
||||||
// raw block cache
|
// raw block cache
|
||||||
// init cacheBlockNumber_to invalid SD block number
|
uint32_t SdVolume::cacheBlockNumber_; // current block number
|
||||||
uint32_t SdVolume::cacheBlockNumber_ = 0XFFFFFFFF;
|
cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card
|
||||||
cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card
|
Sd2Card* SdVolume::sdCard_; // pointer to SD card object
|
||||||
Sd2Card* SdVolume::sdCard_; // pointer to SD card object
|
bool SdVolume::cacheDirty_; // cacheFlush() will write block if true
|
||||||
uint8_t SdVolume::cacheDirty_ = 0; // cacheFlush() will write block if true
|
uint32_t SdVolume::cacheMirrorBlock_; // mirror block for second FAT
|
||||||
uint32_t SdVolume::cacheMirrorBlock_ = 0; // mirror block for second FAT
|
#endif // USE_MULTIPLE_CARDS
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// find a contiguous group of clusters
|
// find a contiguous group of clusters
|
||||||
uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
|
bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
|
||||||
// start of group
|
// start of group
|
||||||
uint32_t bgnCluster;
|
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
|
// flag to save place to start next search
|
||||||
uint8_t setStart;
|
bool setStart;
|
||||||
|
|
||||||
// set search start cluster
|
// set search start cluster
|
||||||
if (*curCluster) {
|
if (*curCluster) {
|
||||||
|
@ -47,25 +52,22 @@ uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
|
||||||
bgnCluster = allocSearchStart_;
|
bgnCluster = allocSearchStart_;
|
||||||
|
|
||||||
// save next search start if one cluster
|
// save next search start if one cluster
|
||||||
setStart = 1 == count;
|
setStart = count == 1;
|
||||||
}
|
}
|
||||||
// end of group
|
// end of group
|
||||||
uint32_t endCluster = bgnCluster;
|
endCluster = bgnCluster;
|
||||||
|
|
||||||
// last cluster of FAT
|
|
||||||
uint32_t fatEnd = clusterCount_ + 1;
|
|
||||||
|
|
||||||
// search the FAT for free clusters
|
// search the FAT for free clusters
|
||||||
for (uint32_t n = 0;; n++, endCluster++) {
|
for (uint32_t n = 0;; n++, endCluster++) {
|
||||||
// can't find space checked all clusters
|
// can't find space checked all clusters
|
||||||
if (n >= clusterCount_) return false;
|
if (n >= clusterCount_) goto fail;
|
||||||
|
|
||||||
// past end - start from beginning of FAT
|
// past end - start from beginning of FAT
|
||||||
if (endCluster > fatEnd) {
|
if (endCluster > fatEnd) {
|
||||||
bgnCluster = endCluster = 2;
|
bgnCluster = endCluster = 2;
|
||||||
}
|
}
|
||||||
uint32_t f;
|
uint32_t f;
|
||||||
if (!fatGet(endCluster, &f)) return false;
|
if (!fatGet(endCluster, &f)) goto fail;
|
||||||
|
|
||||||
if (f != 0) {
|
if (f != 0) {
|
||||||
// cluster in use try next cluster as bgnCluster
|
// cluster in use try next cluster as bgnCluster
|
||||||
|
@ -76,16 +78,16 @@ uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// mark end of chain
|
// mark end of chain
|
||||||
if (!fatPutEOC(endCluster)) return false;
|
if (!fatPutEOC(endCluster)) goto fail;
|
||||||
|
|
||||||
// link clusters
|
// link clusters
|
||||||
while (endCluster > bgnCluster) {
|
while (endCluster > bgnCluster) {
|
||||||
if (!fatPut(endCluster - 1, endCluster)) return false;
|
if (!fatPut(endCluster - 1, endCluster)) goto fail;
|
||||||
endCluster--;
|
endCluster--;
|
||||||
}
|
}
|
||||||
if (*curCluster != 0) {
|
if (*curCluster != 0) {
|
||||||
// connect chains
|
// connect chains
|
||||||
if (!fatPut(*curCluster, bgnCluster)) return false;
|
if (!fatPut(*curCluster, bgnCluster)) goto fail;
|
||||||
}
|
}
|
||||||
// return first cluster number to caller
|
// return first cluster number to caller
|
||||||
*curCluster = bgnCluster;
|
*curCluster = bgnCluster;
|
||||||
|
@ -94,66 +96,87 @@ uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
|
||||||
if (setStart) allocSearchStart_ = bgnCluster + 1;
|
if (setStart) allocSearchStart_ = bgnCluster + 1;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
uint8_t SdVolume::cacheFlush(void) {
|
bool SdVolume::cacheFlush() {
|
||||||
if (cacheDirty_) {
|
if (cacheDirty_) {
|
||||||
if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) {
|
if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) {
|
||||||
return false;
|
goto fail;
|
||||||
}
|
}
|
||||||
// mirror FAT tables
|
// mirror FAT tables
|
||||||
if (cacheMirrorBlock_) {
|
if (cacheMirrorBlock_) {
|
||||||
if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) {
|
if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) {
|
||||||
return false;
|
goto fail;
|
||||||
}
|
}
|
||||||
cacheMirrorBlock_ = 0;
|
cacheMirrorBlock_ = 0;
|
||||||
}
|
}
|
||||||
cacheDirty_ = 0;
|
cacheDirty_ = 0;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
uint8_t SdVolume::cacheRawBlock(uint32_t blockNumber, uint8_t action) {
|
bool SdVolume::cacheRawBlock(uint32_t blockNumber, bool dirty) {
|
||||||
if (cacheBlockNumber_ != blockNumber) {
|
if (cacheBlockNumber_ != blockNumber) {
|
||||||
if (!cacheFlush()) return false;
|
if (!cacheFlush()) goto fail;
|
||||||
if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) return false;
|
if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) goto fail;
|
||||||
cacheBlockNumber_ = blockNumber;
|
cacheBlockNumber_ = blockNumber;
|
||||||
}
|
}
|
||||||
cacheDirty_ |= action;
|
if (dirty) cacheDirty_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// cache a zero block for blockNumber
|
|
||||||
uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) {
|
|
||||||
if (!cacheFlush()) return false;
|
|
||||||
|
|
||||||
// loop take less flash than memset(cacheBuffer_.data, 0, 512);
|
fail:
|
||||||
for (uint16_t i = 0; i < 512; i++) {
|
return false;
|
||||||
cacheBuffer_.data[i] = 0;
|
|
||||||
}
|
|
||||||
cacheBlockNumber_ = blockNumber;
|
|
||||||
cacheSetDirty();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// return the size in bytes of a cluster chain
|
// return the size in bytes of a cluster chain
|
||||||
uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const {
|
bool SdVolume::chainSize(uint32_t cluster, uint32_t* size) {
|
||||||
uint32_t s = 0;
|
uint32_t s = 0;
|
||||||
do {
|
do {
|
||||||
if (!fatGet(cluster, &cluster)) return false;
|
if (!fatGet(cluster, &cluster)) goto fail;
|
||||||
s += 512UL << clusterSizeShift_;
|
s += 512UL << clusterSizeShift_;
|
||||||
} while (!isEOC(cluster));
|
} while (!isEOC(cluster));
|
||||||
*size = s;
|
*size = s;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Fetch a FAT entry
|
// Fetch a FAT entry
|
||||||
uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const {
|
bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) {
|
||||||
if (cluster > (clusterCount_ + 1)) return false;
|
uint32_t lba;
|
||||||
uint32_t lba = fatStartBlock_;
|
if (cluster > (clusterCount_ + 1)) goto fail;
|
||||||
lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7;
|
if (FAT12_SUPPORT && fatType_ == 12) {
|
||||||
|
uint16_t index = cluster;
|
||||||
|
index += index >> 1;
|
||||||
|
lba = fatStartBlock_ + (index >> 9);
|
||||||
|
if (!cacheRawBlock(lba, CACHE_FOR_READ)) goto fail;
|
||||||
|
index &= 0X1FF;
|
||||||
|
uint16_t tmp = cacheBuffer_.data[index];
|
||||||
|
index++;
|
||||||
|
if (index == 512) {
|
||||||
|
if (!cacheRawBlock(lba + 1, CACHE_FOR_READ)) goto fail;
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
tmp |= cacheBuffer_.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 {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
if (lba != cacheBlockNumber_) {
|
if (lba != cacheBlockNumber_) {
|
||||||
if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false;
|
if (!cacheRawBlock(lba, CACHE_FOR_READ)) goto fail;
|
||||||
}
|
}
|
||||||
if (fatType_ == 16) {
|
if (fatType_ == 16) {
|
||||||
*value = cacheBuffer_.fat16[cluster & 0XFF];
|
*value = cacheBuffer_.fat16[cluster & 0XFF];
|
||||||
|
@ -161,56 +184,127 @@ uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const {
|
||||||
*value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK;
|
*value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Store a FAT entry
|
// Store a FAT entry
|
||||||
uint8_t SdVolume::fatPut(uint32_t cluster, uint32_t value) {
|
bool SdVolume::fatPut(uint32_t cluster, uint32_t value) {
|
||||||
|
uint32_t lba;
|
||||||
// error if reserved cluster
|
// error if reserved cluster
|
||||||
if (cluster < 2) return false;
|
if (cluster < 2) goto fail;
|
||||||
|
|
||||||
// error if not in FAT
|
// error if not in FAT
|
||||||
if (cluster > (clusterCount_ + 1)) return false;
|
if (cluster > (clusterCount_ + 1)) goto fail;
|
||||||
|
|
||||||
// calculate block address for entry
|
if (FAT12_SUPPORT && fatType_ == 12) {
|
||||||
uint32_t lba = fatStartBlock_;
|
uint16_t index = cluster;
|
||||||
lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7;
|
index += index >> 1;
|
||||||
|
lba = fatStartBlock_ + (index >> 9);
|
||||||
if (lba != cacheBlockNumber_) {
|
if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto fail;
|
||||||
if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false;
|
// mirror second FAT
|
||||||
|
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
|
||||||
|
index &= 0X1FF;
|
||||||
|
uint8_t tmp = value;
|
||||||
|
if (cluster & 1) {
|
||||||
|
tmp = (cacheBuffer_.data[index] & 0XF) | tmp << 4;
|
||||||
|
}
|
||||||
|
cacheBuffer_.data[index] = tmp;
|
||||||
|
index++;
|
||||||
|
if (index == 512) {
|
||||||
|
lba++;
|
||||||
|
index = 0;
|
||||||
|
if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto fail;
|
||||||
|
// mirror second FAT
|
||||||
|
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
|
||||||
|
}
|
||||||
|
tmp = value >> 4;
|
||||||
|
if (!(cluster & 1)) {
|
||||||
|
tmp = ((cacheBuffer_.data[index] & 0XF0)) | tmp >> 4;
|
||||||
|
}
|
||||||
|
cacheBuffer_.data[index] = tmp;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
if (fatType_ == 16) {
|
||||||
|
lba = fatStartBlock_ + (cluster >> 8);
|
||||||
|
} else if (fatType_ == 32) {
|
||||||
|
lba = fatStartBlock_ + (cluster >> 7);
|
||||||
|
} else {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto fail;
|
||||||
// store entry
|
// store entry
|
||||||
if (fatType_ == 16) {
|
if (fatType_ == 16) {
|
||||||
cacheBuffer_.fat16[cluster & 0XFF] = value;
|
cacheBuffer_.fat16[cluster & 0XFF] = value;
|
||||||
} else {
|
} else {
|
||||||
cacheBuffer_.fat32[cluster & 0X7F] = value;
|
cacheBuffer_.fat32[cluster & 0X7F] = value;
|
||||||
}
|
}
|
||||||
cacheSetDirty();
|
|
||||||
|
|
||||||
// mirror second FAT
|
// mirror second FAT
|
||||||
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
|
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// free a cluster chain
|
// free a cluster chain
|
||||||
uint8_t SdVolume::freeChain(uint32_t cluster) {
|
bool SdVolume::freeChain(uint32_t cluster) {
|
||||||
|
uint32_t next;
|
||||||
|
|
||||||
// clear free cluster location
|
// clear free cluster location
|
||||||
allocSearchStart_ = 2;
|
allocSearchStart_ = 2;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
uint32_t next;
|
if (!fatGet(cluster, &next)) goto fail;
|
||||||
if (!fatGet(cluster, &next)) return false;
|
|
||||||
|
|
||||||
// free cluster
|
// free cluster
|
||||||
if (!fatPut(cluster, 0)) return false;
|
if (!fatPut(cluster, 0)) goto fail;
|
||||||
|
|
||||||
cluster = next;
|
cluster = next;
|
||||||
} while (!isEOC(cluster));
|
} while (!isEOC(cluster));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/**
|
/** Volume free space in clusters.
|
||||||
* Initialize a FAT volume.
|
*
|
||||||
|
* \return Count of free clusters for success or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int32_t SdVolume::freeClusterCount() {
|
||||||
|
uint32_t free = 0;
|
||||||
|
uint16_t n;
|
||||||
|
uint32_t todo = clusterCount_ + 2;
|
||||||
|
|
||||||
|
if (fatType_ == 16) {
|
||||||
|
n = 256;
|
||||||
|
} else if (fatType_ == 32) {
|
||||||
|
n = 128;
|
||||||
|
} else {
|
||||||
|
// put FAT12 here
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t lba = fatStartBlock_; todo; todo -= n, lba++) {
|
||||||
|
if (!cacheRawBlock(lba, CACHE_FOR_READ)) return -1;
|
||||||
|
if (todo < n) n = todo;
|
||||||
|
if (fatType_ == 16) {
|
||||||
|
for (uint16_t i = 0; i < n; i++) {
|
||||||
|
if (cacheBuffer_.fat16[i] == 0) free++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (uint16_t i = 0; i < n; i++) {
|
||||||
|
if (cacheBuffer_.fat32[i] == 0) free++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return free;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** Initialize a FAT volume.
|
||||||
*
|
*
|
||||||
* \param[in] dev The SD card where the volume is located.
|
* \param[in] dev The SD card where the volume is located.
|
||||||
*
|
*
|
||||||
|
@ -224,58 +318,66 @@ uint8_t SdVolume::freeChain(uint32_t cluster) {
|
||||||
* failure include not finding a valid partition, not finding a valid
|
* failure include not finding a valid partition, not finding a valid
|
||||||
* FAT file system in the specified partition or an I/O error.
|
* FAT file system in the specified partition or an I/O error.
|
||||||
*/
|
*/
|
||||||
uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) {
|
bool SdVolume::init(Sd2Card* dev, uint8_t part) {
|
||||||
|
uint32_t totalBlocks;
|
||||||
uint32_t volumeStartBlock = 0;
|
uint32_t volumeStartBlock = 0;
|
||||||
|
fat32_boot_t* fbs;
|
||||||
|
|
||||||
sdCard_ = dev;
|
sdCard_ = dev;
|
||||||
|
fatType_ = 0;
|
||||||
|
allocSearchStart_ = 2;
|
||||||
|
cacheDirty_ = 0; // cacheFlush() will write block if true
|
||||||
|
cacheMirrorBlock_ = 0;
|
||||||
|
cacheBlockNumber_ = 0XFFFFFFFF;
|
||||||
|
|
||||||
// if part == 0 assume super floppy with FAT boot sector in block zero
|
// if part == 0 assume super floppy with FAT boot sector in block zero
|
||||||
// if part > 0 assume mbr volume with partition table
|
// if part > 0 assume mbr volume with partition table
|
||||||
if (part) {
|
if (part) {
|
||||||
if (part > 4)return false;
|
if (part > 4)goto fail;
|
||||||
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false;
|
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto fail;
|
||||||
part_t* p = &cacheBuffer_.mbr.part[part-1];
|
part_t* p = &cacheBuffer_.mbr.part[part-1];
|
||||||
if ((p->boot & 0X7F) !=0 ||
|
if ((p->boot & 0X7F) !=0 ||
|
||||||
p->totalSectors < 100 ||
|
p->totalSectors < 100 ||
|
||||||
p->firstSector == 0) {
|
p->firstSector == 0) {
|
||||||
// not a valid partition
|
// not a valid partition
|
||||||
return false;
|
goto fail;
|
||||||
}
|
}
|
||||||
volumeStartBlock = p->firstSector;
|
volumeStartBlock = p->firstSector;
|
||||||
}
|
}
|
||||||
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false;
|
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto fail;
|
||||||
bpb_t* bpb = &cacheBuffer_.fbs.bpb;
|
fbs = &cacheBuffer_.fbs32;
|
||||||
if (bpb->bytesPerSector != 512 ||
|
if (fbs->bytesPerSector != 512 ||
|
||||||
bpb->fatCount == 0 ||
|
fbs->fatCount == 0 ||
|
||||||
bpb->reservedSectorCount == 0 ||
|
fbs->reservedSectorCount == 0 ||
|
||||||
bpb->sectorsPerCluster == 0) {
|
fbs->sectorsPerCluster == 0) {
|
||||||
// not valid FAT volume
|
// not valid FAT volume
|
||||||
return false;
|
goto fail;
|
||||||
}
|
}
|
||||||
fatCount_ = bpb->fatCount;
|
fatCount_ = fbs->fatCount;
|
||||||
blocksPerCluster_ = bpb->sectorsPerCluster;
|
blocksPerCluster_ = fbs->sectorsPerCluster;
|
||||||
|
|
||||||
// determine shift that is same as multiply by blocksPerCluster_
|
// determine shift that is same as multiply by blocksPerCluster_
|
||||||
clusterSizeShift_ = 0;
|
clusterSizeShift_ = 0;
|
||||||
while (blocksPerCluster_ != (1 << clusterSizeShift_)) {
|
while (blocksPerCluster_ != (1 << clusterSizeShift_)) {
|
||||||
// error if not power of 2
|
// error if not power of 2
|
||||||
if (clusterSizeShift_++ > 7) return false;
|
if (clusterSizeShift_++ > 7) goto fail;
|
||||||
}
|
}
|
||||||
blocksPerFat_ = bpb->sectorsPerFat16 ?
|
blocksPerFat_ = fbs->sectorsPerFat16 ?
|
||||||
bpb->sectorsPerFat16 : bpb->sectorsPerFat32;
|
fbs->sectorsPerFat16 : fbs->sectorsPerFat32;
|
||||||
|
|
||||||
fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount;
|
fatStartBlock_ = volumeStartBlock + fbs->reservedSectorCount;
|
||||||
|
|
||||||
// count for FAT16 zero for FAT32
|
// count for FAT16 zero for FAT32
|
||||||
rootDirEntryCount_ = bpb->rootDirEntryCount;
|
rootDirEntryCount_ = fbs->rootDirEntryCount;
|
||||||
|
|
||||||
// directory start for FAT16 dataStart for FAT32
|
// directory start for FAT16 dataStart for FAT32
|
||||||
rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_;
|
rootDirStart_ = fatStartBlock_ + fbs->fatCount * blocksPerFat_;
|
||||||
|
|
||||||
// data start for FAT16 and FAT32
|
// data start for FAT16 and FAT32
|
||||||
dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511)/512);
|
dataStartBlock_ = rootDirStart_ + ((32 * fbs->rootDirEntryCount + 511)/512);
|
||||||
|
|
||||||
// total blocks for FAT16 or FAT32
|
// total blocks for FAT16 or FAT32
|
||||||
uint32_t totalBlocks = bpb->totalSectors16 ?
|
totalBlocks = fbs->totalSectors16 ?
|
||||||
bpb->totalSectors16 : bpb->totalSectors32;
|
fbs->totalSectors16 : fbs->totalSectors32;
|
||||||
// total data blocks
|
// total data blocks
|
||||||
clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock);
|
clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock);
|
||||||
|
|
||||||
|
@ -285,11 +387,15 @@ uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) {
|
||||||
// FAT type is determined by cluster count
|
// FAT type is determined by cluster count
|
||||||
if (clusterCount_ < 4085) {
|
if (clusterCount_ < 4085) {
|
||||||
fatType_ = 12;
|
fatType_ = 12;
|
||||||
|
if (!FAT12_SUPPORT) goto fail;
|
||||||
} else if (clusterCount_ < 65525) {
|
} else if (clusterCount_ < 65525) {
|
||||||
fatType_ = 16;
|
fatType_ = 16;
|
||||||
} else {
|
} else {
|
||||||
rootDirStart_ = bpb->fat32RootCluster;
|
rootDirStart_ = fbs->fat32RootCluster;
|
||||||
fatType_ = 32;
|
fatType_ = 32;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
211
Marlin/SdVolume.h
Normal file
211
Marlin/SdVolume.h
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
/* Arduino SdFat Library
|
||||||
|
* Copyright (C) 2009 by William Greiman
|
||||||
|
*
|
||||||
|
* This file is part of the Arduino SdFat Library
|
||||||
|
*
|
||||||
|
* This Library is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This Library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with the Arduino SdFat Library. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef SdVolume_h
|
||||||
|
#define SdVolume_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief SdVolume class
|
||||||
|
*/
|
||||||
|
#include "SdFatConfig.h"
|
||||||
|
#include "Sd2Card.h"
|
||||||
|
#include "SdFatStructs.h"
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// 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 (!cacheFlush()) 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;
|
||||||
|
|
||||||
|
// value for dirty argument in cacheRawBlock to indicate read from cache
|
||||||
|
static bool const CACHE_FOR_READ = false;
|
||||||
|
// value for dirty argument in cacheRawBlock to indicate write to cache
|
||||||
|
static bool const CACHE_FOR_WRITE = true;
|
||||||
|
|
||||||
|
#if USE_MULTIPLE_CARDS
|
||||||
|
cache_t cacheBuffer_; // 512 byte cache for device blocks
|
||||||
|
uint32_t cacheBlockNumber_; // Logical number of block in the cache
|
||||||
|
Sd2Card* sdCard_; // Sd2Card object for cache
|
||||||
|
bool cacheDirty_; // cacheFlush() will write block if true
|
||||||
|
uint32_t cacheMirrorBlock_; // block number for mirror FAT
|
||||||
|
#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 Sd2Card* sdCard_; // Sd2Card object for cache
|
||||||
|
static bool cacheDirty_; // cacheFlush() will write block if true
|
||||||
|
static uint32_t cacheMirrorBlock_; // block number for mirror FAT
|
||||||
|
#endif // USE_MULTIPLE_CARDS
|
||||||
|
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
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
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 {
|
||||||
|
return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);}
|
||||||
|
uint32_t blockNumber(uint32_t cluster, uint32_t position) const {
|
||||||
|
return clusterStartBlock(cluster) + blockOfCluster(position);}
|
||||||
|
cache_t *cache() {return &cacheBuffer_;}
|
||||||
|
uint32_t cacheBlockNumber() {return cacheBlockNumber_;}
|
||||||
|
#if USE_MULTIPLE_CARDS
|
||||||
|
bool cacheFlush();
|
||||||
|
bool cacheRawBlock(uint32_t blockNumber, bool dirty);
|
||||||
|
#else // USE_MULTIPLE_CARDS
|
||||||
|
static bool cacheFlush();
|
||||||
|
static bool cacheRawBlock(uint32_t blockNumber, bool dirty);
|
||||||
|
#endif // USE_MULTIPLE_CARDS
|
||||||
|
// used by SdBaseFile write to assign cache to SD location
|
||||||
|
void cacheSetBlockNumber(uint32_t blockNumber, bool dirty) {
|
||||||
|
cacheDirty_ = dirty;
|
||||||
|
cacheBlockNumber_ = blockNumber;
|
||||||
|
}
|
||||||
|
void cacheSetDirty() {cacheDirty_ |= CACHE_FOR_WRITE;}
|
||||||
|
bool chainSize(uint32_t beginCluster, uint32_t* size);
|
||||||
|
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
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#ifdef SDSUPPORT
|
#ifdef SDSUPPORT
|
||||||
|
|
||||||
|
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
|
|
||||||
class CardReader
|
class CardReader
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
files to compare manually:
|
|
||||||
planner.cpp
|
|
||||||
stepper.cpp
|
|
||||||
temperature.cpp
|
|
||||||
|
|
||||||
---
|
|
||||||
things that changed:
|
|
||||||
* planner.cpp
|
|
||||||
estimate_acc_distance now works with floats.
|
|
||||||
in calculate_trapezoid:for_block
|
|
||||||
long acceleration_rate=(long)((float)acceleration*8.388608) is gone
|
|
||||||
so is block_>acceleration_rate
|
|
||||||
void planner_reverse_pass:
|
|
||||||
some stuff I don't understand right now changed
|
|
||||||
in planner_forward_pass:
|
|
||||||
done: BLOCK_BUFFER_SIZE is now necessarily power of 2 (aka 8 16, 32). Inportant to document this somewhere.
|
|
||||||
no more inline in void plan_discard_current_block()
|
|
||||||
no more inline in plan_get_current_block()
|
|
||||||
in plan_buffer_line(...)
|
|
||||||
the long target[4]; and calculations of thoose should go after the while(block_buffer_tail==..). if the axis_steps_per_unit are changed from the gcode (M92) the calculation for the currently planned buffer move will be corrupt, because Target is calculated with one value, and the stuff afterwards with another. At least this solved the problem I had with the M92 E* changes in the code. Very sure about this, I took me 20min to find this as the solution for the bug I was hunting.
|
|
||||||
around if(feed_rate<minimumfeedrate) this only should be done if it is not a pure extrusion. I think there is a bug right now.
|
|
||||||
~line 447 blockcount=
|
|
||||||
not sure if this also works if the difference is negative, as it would happen if the ringbuffer runs over the end and start at 0.
|
|
||||||
~line 507 tmp_aceleration. not sure whats going on, but a lot changed.
|
|
||||||
|
|
||||||
|
|
||||||
* stepper.cpp
|
|
||||||
~214: if (busy) should be a echoln, maybe
|
|
||||||
~331: great, The Z_M_PIN checks are in :)
|
|
||||||
|
|
||||||
*temperature.cpp
|
|
||||||
done: enum for heater, bed,
|
|
||||||
manage_heater() is seriously different.
|
|
||||||
done: if tem_meas_ready ==true->!true+return?
|
|
||||||
done #define K1 0.95 maybe in the configuration.h?
|
|
||||||
semi-done: PID-C checking needed. Untested but added.
|
|
||||||
----
|
|
||||||
|
|
||||||
still needed to finish the merge, before testin!
|
|
||||||
|
|
||||||
manage_heater
|
|
||||||
ISR
|
|
||||||
movement planner
|
|
||||||
|
|
||||||
TODO:
|
|
||||||
|
|
||||||
remove traveling at maxpseed
|
|
||||||
remove Simplelcd
|
|
||||||
|
|
||||||
remove DEBUG_STEPS?
|
|
||||||
|
|
||||||
block_t
|
|
||||||
pid_dt ->0.1 whats the changes to the PID, checking needed
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
second merge saturday morning:
|
|
||||||
done: PID_dt->0.1
|
|
Loading…
Reference in a new issue