*/ | */ | ||||
#include <SD.h> | #include <SD.h> | ||||
#ifndef __SD_t3_H__ | |||||
/* for debugging file open/close leaks | /* for debugging file open/close leaks | ||||
uint8_t nfilecount=0; | uint8_t nfilecount=0; | ||||
return _file->isOpen(); | return _file->isOpen(); | ||||
return false; | return false; | ||||
} | } | ||||
#endif |
*/ | */ | ||||
#include "SD.h" | #include "SD.h" | ||||
#ifndef __SD_t3_H__ | |||||
// Used by `getNextPathComponent` | // Used by `getNextPathComponent` | ||||
#define MAX_COMPONENT_LEN 12 // What is max length? | #define MAX_COMPONENT_LEN 12 // What is max length? | ||||
} | } | ||||
SDClass SD; | SDClass SD; | ||||
#endif |
#if defined(__arm__) | |||||
#include "SD_t3.h" | |||||
#endif | |||||
/* | /* | ||||
SD - a slightly more friendly wrapper for sdfatlib | SD - a slightly more friendly wrapper for sdfatlib |
/* Optimized SD Library for Teensy 3.X | |||||
* Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com | |||||
* | |||||
* Development of this SD library was funded by PJRC.COM, LLC by sales of | |||||
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
* open source software by purchasing genuine Teensy or other PJRC products. | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice, development funding notice, and this permission | |||||
* notice shall be included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
*/ | |||||
// This Teensy 3.x optimized version is a work-in-progress. | |||||
// Uncomment this line to use the Teensy version. Otherwise, | |||||
// the normal SD library is used. | |||||
//#define USE_TEENSY3_OPTIMIZED_CODE | |||||
/* Why reinvent the SD library wheel... | |||||
* 1: Allow reading files from within interrupts | |||||
* 2: Cache more than one sector for improved performance | |||||
* 3: General optimization for 32 bit ARM on Teensy 3.x & Teensy-LC | |||||
* 4: Permissive MIT license | |||||
*/ | |||||
#if !defined(__SD_t3_H__) && defined(__arm__) && defined(USE_TEENSY3_OPTIMIZED_CODE) | |||||
#define __SD_t3_H__ | |||||
#define __SD_H__ | |||||
#include <Arduino.h> | |||||
#include <SPI.h> | |||||
#include "utility/ioreg.h" | |||||
#define SD_CACHE_SIZE 7 // each cache entry uses 520 bytes of RAM | |||||
#define SD_SPI_SPEED SPISettings(25000000, MSBFIRST, SPI_MODE0) | |||||
#define FILE_READ 0 | |||||
#define FILE_WRITE 1 | |||||
#define FILE_DIR 2 | |||||
#define FILE_DIR_ROOT16 3 | |||||
#define FILE_INVALID 4 | |||||
class File; | |||||
class SDClass | |||||
{ | |||||
public: | |||||
static bool begin(uint8_t csPin = SS); | |||||
static File open(const char *path, uint8_t mode = FILE_READ); | |||||
static bool exists(const char *path); | |||||
static bool mkdir(const char *path); | |||||
static bool remove(const char *path); | |||||
static bool rmdir(const char *path); | |||||
private: | |||||
static uint8_t sd_cmd0(); | |||||
static uint32_t sd_cmd8(); | |||||
static uint8_t sd_acmd41(uint32_t hcs); | |||||
static uint32_t sd_cmd58(); | |||||
static bool sd_read(uint32_t addr, void * data); | |||||
static void send_cmd(uint16_t cmd, uint32_t arg); | |||||
static uint8_t recv_r1(); | |||||
static uint32_t recv_r3_or_r7(); | |||||
static void end_cmd(); | |||||
static volatile IO_REG_TYPE * csreg; | |||||
static IO_REG_TYPE csmask; | |||||
static uint8_t card_type; // 1=SDv1, 2=SDv2, 3=SDHC | |||||
static File rootDir; | |||||
static uint32_t fat1_begin_lba; | |||||
static uint32_t fat2_begin_lba; | |||||
static uint32_t data_begin_lba; | |||||
static uint32_t max_cluster; | |||||
static uint8_t sector2cluster; | |||||
static uint8_t fat_type; | |||||
friend class SDCache; | |||||
friend class File; | |||||
typedef struct { | |||||
union { | |||||
struct { // short 8.3 filename info | |||||
char name[11]; | |||||
uint8_t attrib; | |||||
uint8_t reserved; | |||||
uint8_t ctime_tenth; | |||||
uint16_t ctime; | |||||
uint16_t cdate; | |||||
uint16_t adate; | |||||
uint16_t cluster_high; | |||||
uint16_t wtime; | |||||
uint16_t wdate; | |||||
uint16_t cluster_low; | |||||
uint32_t size; | |||||
}; | |||||
struct { // long filename info | |||||
uint8_t ord; | |||||
uint8_t lname1[10]; | |||||
uint8_t lattrib; | |||||
uint8_t type; | |||||
uint8_t cksum; | |||||
uint8_t lname2[12]; | |||||
uint16_t lcluster_low; | |||||
uint8_t lname3[4]; | |||||
}; | |||||
}; | |||||
} fatdir_t; | |||||
typedef union { | |||||
uint8_t u8[512]; | |||||
uint16_t u16[256]; | |||||
uint32_t u32[128]; | |||||
fatdir_t dir[16]; | |||||
} sector_t; | |||||
}; | |||||
#define ATTR_READ_ONLY 0x01 | |||||
#define ATTR_HIDDEN 0x02 | |||||
#define ATTR_SYSTEM 0x04 | |||||
#define ATTR_VOLUME_ID 0x08 | |||||
#define ATTR_DIRECTORY 0x10 | |||||
#define ATTR_ARCHIVE 0x20 | |||||
#define ATTR_LONG_NAME 0x0F | |||||
class File : public Stream | |||||
{ | |||||
public: | |||||
File(); | |||||
~File(); | |||||
// TODO: copy constructors, needs to be ISR safe | |||||
virtual size_t write(uint8_t b); | |||||
virtual size_t write(const uint8_t *buf, size_t size); | |||||
virtual int read(); | |||||
virtual int peek(); | |||||
virtual int available(); | |||||
virtual void flush(); | |||||
int read(void *buf, uint32_t size); | |||||
bool seek(uint32_t pos); | |||||
uint32_t position() { | |||||
if (type <= FILE_WRITE) return offset; | |||||
return 0; | |||||
} | |||||
uint32_t size() { | |||||
if (type <= FILE_WRITE) return length; | |||||
return 0; | |||||
} | |||||
void close(); | |||||
operator bool() { | |||||
return (type < FILE_INVALID); | |||||
} | |||||
char * name(); | |||||
bool isDirectory() { | |||||
return (type == FILE_DIR) || (type == FILE_DIR_ROOT16); | |||||
} | |||||
File openNextFile(uint8_t mode = FILE_READ); | |||||
void rewindDirectory() { | |||||
rewind(); | |||||
} | |||||
using Print::write; | |||||
void rewind() { | |||||
offset = 0; | |||||
current_cluster = start_cluster; | |||||
}; | |||||
private: | |||||
bool find(const char *filename, File *found); | |||||
void init(SDClass::fatdir_t *dirent); | |||||
bool next_cluster(); | |||||
uint32_t offset; // position within file (EOF = length) | |||||
uint32_t length; // total size of file | |||||
uint32_t start_cluster; // first cluster for the file | |||||
uint32_t current_cluster; // position (must agree w/ offset) | |||||
uint32_t dirent_lba; // dir sector for this file | |||||
uint8_t dirent_index; // dir index within sector (0 to 15) | |||||
uint8_t type; // file vs dir | |||||
char namestr[12]; | |||||
friend class SDClass; | |||||
static inline uint32_t cluster_number(uint32_t n) { | |||||
return n >> (SDClass::sector2cluster + 9); | |||||
} | |||||
static inline uint32_t cluster_offset(uint32_t n) { | |||||
return n & ((1 << (SDClass::sector2cluster + 9)) - 1); | |||||
} | |||||
static inline uint32_t custer_to_sector(uint32_t n) { | |||||
return (n - 2) * (1 << SDClass::sector2cluster) | |||||
+ SDClass::data_begin_lba; | |||||
} | |||||
static inline bool is_new_cluster(uint32_t lba) { | |||||
return (lba & ((1 << SDClass::sector2cluster) - 1)) == 0; | |||||
} | |||||
}; | |||||
class SDCache | |||||
{ | |||||
private: | |||||
// SDCache objects should be created with local scope. | |||||
// read(), get(), alloc() acquire temporary locks on | |||||
// cache buffers, which are automatically released | |||||
// by the destructor when the object goes out of scope. | |||||
// Pointers returned by those functions must NEVER be | |||||
// used after the SDCache object which returned them | |||||
// no longer exists. | |||||
SDCache(void) { item = NULL; } | |||||
~SDCache(void) { release(); } | |||||
typedef struct { | |||||
SDClass::sector_t data; | |||||
uint32_t lba; | |||||
uint8_t usagecount; | |||||
uint8_t priority; | |||||
uint8_t flags; | |||||
} cache_t; | |||||
SDClass::sector_t * read(uint32_t lba, bool is_fat=false); | |||||
bool read(uint32_t lba, void *buffer); | |||||
SDClass::sector_t * alloc(uint32_t lba); | |||||
void priority(signed int n); | |||||
void priority(uint32_t lba, signed int n); | |||||
void dirty(void); | |||||
void flush(void); | |||||
void release(void); | |||||
cache_t * find(uint32_t lba); | |||||
cache_t * empty(); | |||||
cache_t * item; | |||||
static cache_t cache[SD_CACHE_SIZE]; | |||||
friend class SDClass; | |||||
friend class File; | |||||
}; | |||||
extern SDClass SD; | |||||
#endif |
/* Optimized SD Library for Teensy 3.X | |||||
* Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com | |||||
* | |||||
* Development of this SD library was funded by PJRC.COM, LLC by sales of | |||||
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
* open source software by purchasing genuine Teensy or other PJRC products. | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice, development funding notice, and this permission | |||||
* notice shall be included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
*/ | |||||
#if defined(__arm__) | |||||
#include "SD_t3.h" | |||||
#ifdef USE_TEENSY3_OPTIMIZED_CODE | |||||
#define cache_t SDCache::cache_t | |||||
#define sector_t SDClass::sector_t | |||||
cache_t SDCache::cache[SD_CACHE_SIZE]; | |||||
#define CACHE_FLAG_HAS_DATA 1 | |||||
#define CACHE_FLAG_IS_DIRTY 2 | |||||
#define CACHE_FLAG_IS_FAT 4 | |||||
/* | |||||
static void print_sector(const void *data) | |||||
{ | |||||
const uint8_t *p = (const uint8_t *)data; | |||||
for (int i=0; i < 512; i++) { | |||||
Serial.printf(" %02X", *p++); | |||||
if ((i & 31) == 31) Serial.println(); | |||||
} | |||||
} | |||||
*/ | |||||
// Read a sector into the cache. If the sector is already cached, | |||||
// of course no actual read occurs. This is the primary function | |||||
// used to access the SD card. | |||||
// | |||||
sector_t * SDCache::read(uint32_t lba, bool is_fat) | |||||
{ | |||||
sector_t *ret = NULL; | |||||
//uint32_t slot=0, ucount=0; | |||||
// the entire read operation, including all cache manipulation, | |||||
// needs to be protected with exclusive access to the hardware. | |||||
//Serial.printf("cache read: lba = %d\n", lba); | |||||
SPI.beginTransaction(SD_SPI_SPEED); | |||||
// does the cache already have the sector? | |||||
cache_t *c = find(lba); | |||||
if (c) { | |||||
ret = &c->data; | |||||
} else { | |||||
c = empty(); | |||||
if (c != NULL) { | |||||
// TODO: if dirty, write to SD card | |||||
if (SDClass::sd_read(lba, &c->data)) { | |||||
item = c; | |||||
c->lba = lba; | |||||
c->usagecount = 1; | |||||
c->flags = CACHE_FLAG_HAS_DATA; | |||||
if (is_fat) c->flags |= CACHE_FLAG_IS_FAT; | |||||
ret = &c->data; | |||||
//Serial.printf("cache read %u\n", lba); | |||||
//print_sector(&c->data); | |||||
} | |||||
} | |||||
} | |||||
//if (c) slot = c - cache, ucount = c->usagecount; | |||||
SPI.endTransaction(); | |||||
//if (ret) { | |||||
//Serial.printf("read %u, %u, slot %u\n", lba, ucount, slot); | |||||
//} else { | |||||
//Serial.printf("read %u, FAIL\n", lba); | |||||
//} | |||||
return ret; | |||||
} | |||||
// Read a whole 512 byte sector directly to memory. If the sector is | |||||
// already cached, of course no actual read occurs and data is copied | |||||
// from the cache. When the sector is not cached, it's transferred | |||||
// directly from SD card to memory, bypassing the cache. | |||||
// | |||||
bool SDCache::read(uint32_t lba, void *buffer) | |||||
{ | |||||
bool ret = true; | |||||
SPI.beginTransaction(SD_SPI_SPEED); | |||||
cache_t *c = find(lba); | |||||
if (!c) ret = SDClass::sd_read(lba, buffer); | |||||
SPI.endTransaction(); | |||||
if (c) memcpy(buffer, &c->data, 512); | |||||
return ret; | |||||
} | |||||
sector_t * SDCache::alloc(uint32_t lba) | |||||
{ | |||||
return NULL; | |||||
} | |||||
void SDCache::priority(signed int n) | |||||
{ | |||||
if (!item) return; | |||||
if (n > 0) { | |||||
__disable_irq(); | |||||
signed int pri = (int)(item->priority) + n; | |||||
if (pri > 255) pri = 255; | |||||
item->priority = pri; | |||||
__enable_irq(); | |||||
} else { | |||||
__disable_irq(); | |||||
signed int pri = (int)(item->priority) + n; | |||||
if (pri < 0) pri = 0; | |||||
item->priority = pri; | |||||
__enable_irq(); | |||||
} | |||||
} | |||||
void SDCache::priority(uint32_t lba, signed int n) | |||||
{ | |||||
// TODO: if any a specific sector is cached, adjust its priority | |||||
} | |||||
void SDCache::dirty(void) | |||||
{ | |||||
__disable_irq(); | |||||
item->usagecount |= CACHE_FLAG_IS_DIRTY; | |||||
__enable_irq(); | |||||
} | |||||
void SDCache::release(void) | |||||
{ | |||||
//Serial.printf("cache release\n"); | |||||
if (item) { | |||||
__disable_irq(); | |||||
item->usagecount--; | |||||
//uint32_t ucount = item->usagecount; | |||||
//uint32_t lba = item->lba; | |||||
//Serial.printf("release %d, %d, slot %u\n", item->lba, item->usagecount, item-cache); | |||||
__enable_irq(); | |||||
item = NULL; | |||||
} | |||||
} | |||||
cache_t * SDCache::find(uint32_t lba) | |||||
{ | |||||
//Serial.printf("SDCache::find, lba=%n\n", lba); | |||||
// have we already acquired a cache entry? | |||||
if (item) { | |||||
//Serial.printf(" item exists, lba=%d\n", item->lba); | |||||
// if it's the desired block, use it | |||||
if (item->lba == lba) return item; | |||||
// if not, release our hold on it | |||||
//Serial.printf("cache find release\n"); | |||||
item->usagecount--; | |||||
item = NULL; | |||||
} | |||||
// does the cache already have the sector we want? | |||||
const cache_t *end=cache+SD_CACHE_SIZE; | |||||
for (cache_t *c = cache; c < end; c++) { | |||||
if ((c->flags) && (c->lba == lba)) { | |||||
//Serial.printf(" item found\n"); | |||||
item = c; | |||||
c->usagecount++; | |||||
return c; | |||||
} | |||||
} | |||||
//Serial.printf(" item not found\n"); | |||||
// the desired sector isn't in the cache | |||||
return NULL; | |||||
} | |||||
cache_t * SDCache::empty(void) | |||||
{ | |||||
const cache_t *end=cache+SD_CACHE_SIZE; | |||||
cache_t *useme = NULL; | |||||
uint32_t lowest_priority = 0xFF; | |||||
for (cache_t *c = cache; c < end; c++) { | |||||
if (c->usagecount == 0 && c->priority < lowest_priority) { | |||||
useme = c; | |||||
lowest_priority = c->priority; | |||||
if (lowest_priority == 0) break; | |||||
} | |||||
} | |||||
return useme; | |||||
} | |||||
#endif | |||||
#endif |
/* Optimized SD Library for Teensy 3.X | |||||
* Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com | |||||
* | |||||
* Development of this SD library was funded by PJRC.COM, LLC by sales of | |||||
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
* open source software by purchasing genuine Teensy or other PJRC products. | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice, development funding notice, and this permission | |||||
* notice shall be included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
*/ | |||||
#if defined(__arm__) | |||||
#include "SD_t3.h" | |||||
#ifdef USE_TEENSY3_OPTIMIZED_CODE | |||||
volatile uint8_t * SDClass::csreg; | |||||
uint8_t SDClass::csmask; | |||||
uint8_t SDClass::card_type; | |||||
#define CMD0_GO_IDLE_STATE 0x4095 // arg=0 | |||||
#define CMD1_SEND_OP_COND 0x41FF | |||||
#define CMD8_SEND_IF_COND 0x4887 // arg=0x1AA | |||||
#define CMD55_APP_CMD 0x77FF | |||||
#define ACMD41_SD_SEND_OP_COND 0x69FF | |||||
#define CMD58_READ_OCR 0x7AFF | |||||
#define CMD17_READ_SINGLE_BLOCK 0x51FF | |||||
#define CMD24_WRITE_BLOCK 0x58FF | |||||
uint8_t SDClass::sd_cmd0() | |||||
{ | |||||
send_cmd(CMD0_GO_IDLE_STATE, 0); | |||||
uint8_t r1 = recv_r1(); | |||||
end_cmd(); | |||||
return r1; | |||||
} | |||||
uint32_t SDClass::sd_cmd8() | |||||
{ | |||||
send_cmd(CMD8_SEND_IF_COND, 0x1AA); | |||||
uint8_t r1 = recv_r1(); | |||||
uint32_t cond = 0x80000000; | |||||
if (r1 == 1) { | |||||
cond = recv_r3_or_r7(); | |||||
//Serial.print(cond, HEX); | |||||
} | |||||
end_cmd(); | |||||
return cond; | |||||
} | |||||
uint8_t SDClass::sd_acmd41(uint32_t hcs) | |||||
{ | |||||
//Serial.print("acmd41:"); | |||||
send_cmd(CMD55_APP_CMD, 0); | |||||
uint8_t r1 = recv_r1(); | |||||
end_cmd(); | |||||
send_cmd(ACMD41_SD_SEND_OP_COND, hcs); | |||||
r1 = recv_r1(); | |||||
end_cmd(); | |||||
return r1; | |||||
} | |||||
uint32_t SDClass::sd_cmd58(void) | |||||
{ | |||||
send_cmd(CMD58_READ_OCR, 0); | |||||
uint8_t r1 = recv_r1(); | |||||
uint32_t ocr = 0; | |||||
if (r1 == 0) ocr = recv_r3_or_r7(); | |||||
end_cmd(); | |||||
return ocr; | |||||
} | |||||
bool SDClass::sd_read(uint32_t addr, void * data) | |||||
{ | |||||
//Serial.printf("sd_read %ld\n", addr); | |||||
if (card_type < 2) addr = addr << 9; | |||||
send_cmd(CMD17_READ_SINGLE_BLOCK, addr); | |||||
uint8_t r1 = recv_r1(); | |||||
if (r1 != 0) { | |||||
end_cmd(); | |||||
return false; | |||||
} | |||||
while (1) { | |||||
uint8_t token = SPI.transfer(0xFF); | |||||
//Serial.printf("t=%02X.", token); | |||||
if (token == 0xFE) break; | |||||
if (token != 0xFF) { | |||||
end_cmd(); | |||||
return false; | |||||
} | |||||
// TODO: timeout | |||||
} | |||||
uint8_t *p = (uint8_t *)data; | |||||
uint8_t *end = p + 510; | |||||
SPI0_PUSHR = 0xFFFF | SPI_PUSHR_CTAS(1); | |||||
SPI0_PUSHR = 0xFFFF | SPI_PUSHR_CTAS(1); | |||||
while (p < end) { | |||||
while (!(SPI0_SR & 0xF0)) ; | |||||
SPI0_PUSHR = 0xFFFF | SPI_PUSHR_CTAS(1); | |||||
uint32_t in = SPI0_POPR; | |||||
*p++ = in >> 8; | |||||
*p++ = in; | |||||
} | |||||
while (!(SPI0_SR & 0xF0)) ; | |||||
uint32_t in = SPI0_POPR; | |||||
*p++ = in >> 8; | |||||
*p++ = in; | |||||
while (!(SPI0_SR & 0xF0)) ; | |||||
SPI0_POPR; // ignore crc | |||||
DIRECT_WRITE_HIGH(csreg, csmask); | |||||
SPI.transfer(0xFF); | |||||
return true; | |||||
// token = 0xFE | |||||
// data, 512 bytes | |||||
// crc, 2 bytes | |||||
} | |||||
void SDClass::send_cmd(uint16_t cmd, uint32_t arg) | |||||
{ | |||||
DIRECT_WRITE_LOW(csreg, csmask); | |||||
SPI.transfer(cmd >> 8); | |||||
SPI.transfer16(arg >> 16); | |||||
SPI.transfer16(arg); | |||||
SPI.transfer(cmd); | |||||
} | |||||
uint8_t SDClass::recv_r1(void) | |||||
{ | |||||
uint8_t ret, count=0; | |||||
do { | |||||
ret = SPI.transfer(0xFF); | |||||
//Serial.print(ret); | |||||
//Serial.print("."); | |||||
if ((ret & 0x80) == 0) break; | |||||
} while (++count < 9); | |||||
return ret; | |||||
} | |||||
uint32_t SDClass::recv_r3_or_r7(void) | |||||
{ | |||||
uint32_t r; | |||||
r = SPI.transfer16(0xFFFF) << 16; | |||||
r |= SPI.transfer16(0xFFFF); | |||||
return r; | |||||
} | |||||
void SDClass::end_cmd(void) | |||||
{ | |||||
DIRECT_WRITE_HIGH(csreg, csmask); | |||||
SPI.transfer(0xFF); | |||||
} | |||||
#endif | |||||
#endif |
/* Optimized SD Library for Teensy 3.X | |||||
* Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com | |||||
* | |||||
* Development of this SD library was funded by PJRC.COM, LLC by sales of | |||||
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
* open source software by purchasing genuine Teensy or other PJRC products. | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice, development funding notice, and this permission | |||||
* notice shall be included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
*/ | |||||
#if defined(__arm__) | |||||
#include "SD_t3.h" | |||||
#ifdef USE_TEENSY3_OPTIMIZED_CODE | |||||
#define sector_t SDClass::sector_t | |||||
#define fatdir_t SDClass::fatdir_t | |||||
File SDClass::open(const char *path, uint8_t mode) | |||||
{ | |||||
File root = rootDir; | |||||
File f; | |||||
if (mode > FILE_READ) return f; | |||||
// TODO: this needs the path traversal feature | |||||
//Serial.println("SDClass::open"); | |||||
//Serial.printf("rootDir.start_cluster = %d\n", rootDir.start_cluster); | |||||
//Serial.printf("root.start_cluster = %d\n", root.start_cluster); | |||||
root.find(path, &f); | |||||
return f; | |||||
} | |||||
File File::openNextFile(uint8_t mode) | |||||
{ | |||||
File f; | |||||
if (mode > FILE_READ) return f; | |||||
return f; | |||||
} | |||||
bool File::find(const char *filename, File *found) | |||||
{ | |||||
bool find_unused = true; | |||||
char name83[11]; | |||||
uint32_t lba, sector_count; | |||||
//Serial.println("File::open"); | |||||
const char *f = filename; | |||||
char *p = name83; | |||||
while (p < name83 + 11) { | |||||
char c = *f++; | |||||
if (c == 0) { | |||||
while (p < name83 + 11) *p++ = ' '; | |||||
break; | |||||
} | |||||
if (c == '.') { | |||||
while (p < name83 + 8) *p++ = ' '; | |||||
continue; | |||||
} | |||||
if (c > 126) continue; | |||||
if (c >= 'a' && c <= 'z') c -= 32; | |||||
*p++ = c; | |||||
} | |||||
//Serial.print("name83 = "); | |||||
//for (uint32_t i=0; i < 11; i++) { | |||||
//Serial.printf(" %02X", name83[i]); | |||||
//} | |||||
//Serial.println(); | |||||
if (type == FILE_DIR_ROOT16) { | |||||
lba = start_cluster; | |||||
sector_count = length >> 9; | |||||
} else if (type == FILE_DIR) { | |||||
current_cluster = start_cluster; | |||||
lba = custer_to_sector(start_cluster); | |||||
sector_count = (1 << SDClass::sector2cluster); | |||||
} else { | |||||
return false; // not a directory | |||||
} | |||||
while (1) { | |||||
for (uint32_t i=0; i < sector_count; i++) { | |||||
SDCache sector; | |||||
sector_t *s = sector.read(lba); | |||||
if (!s) return false; | |||||
fatdir_t *dirent = s->dir; | |||||
for (uint32_t j=0; j < 16; j++) { | |||||
if (dirent->attrib == ATTR_LONG_NAME) { | |||||
// TODO: how to match long names? | |||||
} | |||||
if (memcmp(dirent->name, name83, 11) == 0) { | |||||
//Serial.printf("found 8.3, j=%d\n", j); | |||||
found->init(dirent); | |||||
return true; | |||||
} | |||||
uint8_t b0 = dirent->name[0]; | |||||
if (find_unused && (b0 == 0 || b0 == 0xE5)) { | |||||
found->dirent_lba = lba; | |||||
found->dirent_index = j; | |||||
find_unused = false; | |||||
} | |||||
if (b0 == 0) return false; | |||||
offset += 32; | |||||
dirent++; | |||||
} | |||||
lba++; | |||||
} | |||||
if (type == FILE_DIR_ROOT16) break; | |||||
if (!next_cluster()) break; | |||||
lba = custer_to_sector(current_cluster); | |||||
//Serial.printf(" next lba = %d\n", lba); | |||||
} | |||||
return false; | |||||
} | |||||
void File::init(fatdir_t *dirent) | |||||
{ | |||||
offset = 0; | |||||
length = dirent->size; | |||||
start_cluster = (dirent->cluster_high << 16) | dirent->cluster_low; | |||||
current_cluster = start_cluster; | |||||
type = (dirent->attrib & ATTR_DIRECTORY) ? FILE_DIR : FILE_READ; | |||||
//Serial.printf("File::init, cluster = %d, length = %d\n", start_cluster, length); | |||||
} | |||||
#endif | |||||
#endif |
/* Optimized SD Library for Teensy 3.X | |||||
* Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com | |||||
* | |||||
* Development of this SD library was funded by PJRC.COM, LLC by sales of | |||||
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
* open source software by purchasing genuine Teensy or other PJRC products. | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice, development funding notice, and this permission | |||||
* notice shall be included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
*/ | |||||
#if defined(__arm__) | |||||
#include "SD_t3.h" | |||||
#ifdef USE_TEENSY3_OPTIMIZED_CODE | |||||
bool File::next_cluster() | |||||
{ | |||||
SDCache fat; | |||||
uint32_t lba, cluster; | |||||
lba = SDClass::fat1_begin_lba; | |||||
cluster = current_cluster; | |||||
//Serial.println(); | |||||
//Serial.println("****************************************"); | |||||
//Serial.println(); | |||||
//Serial.printf(" current_cluster = %d\n", cluster); | |||||
if (SDClass::fat_type == 16) { | |||||
SDClass::sector_t *s = fat.read(lba + (cluster >> 8), true); | |||||
if (!s) return false; | |||||
cluster = s->u16[cluster & 255]; | |||||
} else { | |||||
SDClass::sector_t *s = fat.read(lba + (cluster >> 7), true); | |||||
if (!s) return false; | |||||
cluster = s->u32[cluster & 127]; | |||||
} | |||||
//Serial.printf(" new_cluster = %d\n", cluster); | |||||
//Serial.println(); | |||||
//Serial.println("****************************************"); | |||||
//Serial.println(); | |||||
current_cluster = cluster; | |||||
if (cluster > SDClass::max_cluster) return false; | |||||
return true; | |||||
} | |||||
#endif | |||||
#endif |
/* Optimized SD Library for Teensy 3.X | |||||
* Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com | |||||
* | |||||
* Development of this SD library was funded by PJRC.COM, LLC by sales of | |||||
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
* open source software by purchasing genuine Teensy or other PJRC products. | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice, development funding notice, and this permission | |||||
* notice shall be included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
*/ | |||||
#if defined(__arm__) | |||||
#include "SD_t3.h" | |||||
#ifdef USE_TEENSY3_OPTIMIZED_CODE | |||||
#define sector_t SDClass::sector_t | |||||
File::File() | |||||
{ | |||||
type = FILE_INVALID; | |||||
} | |||||
File::~File(void) | |||||
{ | |||||
close(); | |||||
} | |||||
size_t File::write(uint8_t b) | |||||
{ | |||||
return write(&b, 1); | |||||
} | |||||
size_t File::write(const uint8_t *buf, size_t size) | |||||
{ | |||||
if (type != FILE_WRITE) { | |||||
setWriteError(); | |||||
return 0; | |||||
} | |||||
// TODO: a lot of work.... | |||||
return 0; | |||||
} | |||||
int File::read() | |||||
{ | |||||
uint8_t b; | |||||
int ret = read(&b, 1); | |||||
if (ret != 1) return -1; | |||||
return b; | |||||
} | |||||
int File::peek() | |||||
{ | |||||
uint32_t save_offset = offset; | |||||
uint32_t save_cluster = current_cluster; | |||||
uint8_t b; | |||||
int ret = read(&b, 1); | |||||
if (ret != 1) return -1; | |||||
offset = save_offset; | |||||
current_cluster = save_cluster; | |||||
return b; | |||||
} | |||||
int File::available() | |||||
{ | |||||
if (type > FILE_WRITE) return 0; | |||||
uint32_t maxsize = length - offset; | |||||
if (maxsize > 0x7FFFFFFF) maxsize = 0x7FFFFFFF; | |||||
return maxsize; | |||||
} | |||||
void File::flush() | |||||
{ | |||||
} | |||||
int File::read(void *buf, uint32_t size) | |||||
{ | |||||
if (type > FILE_WRITE) return 0; | |||||
uint32_t maxsize = length - offset; | |||||
if (size > maxsize) size = maxsize; | |||||
if (size == 0) return 0; | |||||
uint32_t count = 0; | |||||
uint8_t *dest = (uint8_t *)buf; | |||||
uint32_t lba = custer_to_sector(current_cluster); | |||||
uint32_t sindex = cluster_offset(offset); | |||||
lba += sindex >> 9; | |||||
sindex &= 511; | |||||
if (sindex) { | |||||
// first read starts in the middle of a sector | |||||
do { | |||||
SDCache cache; | |||||
sector_t *sector = cache.read(lba); | |||||
if (!sector) return 0; | |||||
uint32_t n = 512 - sindex; | |||||
if (size < n) { | |||||
// read does not consume all of the sector | |||||
memcpy(dest, sector->u8 + sindex, size); | |||||
offset += size; | |||||
cache.priority(+1); | |||||
return size; | |||||
} else { | |||||
// read fully consumes this sector | |||||
memcpy(dest, sector->u8 + sindex, n); | |||||
dest += n; | |||||
count = n; | |||||
offset += n; | |||||
cache.priority(-1); | |||||
} | |||||
} while (0); | |||||
if (is_new_cluster(++lba)) { | |||||
if (!next_cluster()) return count; | |||||
} | |||||
if (count >= size) return count; | |||||
} | |||||
while (1) { | |||||
// every read starts from the beginning of a sector | |||||
do { | |||||
SDCache cache; | |||||
uint32_t n = size - count; | |||||
if (n < 512) { | |||||
// only part of a sector is needed | |||||
sector_t *sector = cache.read(lba); | |||||
if (!sector) return count; | |||||
memcpy(dest, sector->u8, n); | |||||
offset += n; | |||||
count += n; | |||||
cache.priority(+1); | |||||
return count; | |||||
} else { | |||||
// a full sector is required | |||||
if (!cache.read(lba, dest)) return count; | |||||
dest += 512; | |||||
offset += 512; | |||||
count += 512; | |||||
} | |||||
} while (0); | |||||
if (is_new_cluster(++lba)) { | |||||
if (!next_cluster()) return count; | |||||
} | |||||
if (count >= size) return count; | |||||
} | |||||
} | |||||
bool File::seek(uint32_t pos) | |||||
{ | |||||
if (type > FILE_WRITE) return false; | |||||
if (pos > length) return false; | |||||
uint32_t save_cluster = current_cluster; | |||||
uint32_t count; | |||||
// TODO: if moving to a new lba, lower cache priority | |||||
signed int diff = (int)cluster_number(pos) - (int)cluster_number(offset); | |||||
if (diff >= 0) { | |||||
// seek fowards, 0 or more clusters from current position | |||||
count = diff; | |||||
} else { | |||||
// seek backwards, need to start from beginning of file | |||||
current_cluster = start_cluster; | |||||
count = cluster_number(pos); | |||||
} | |||||
while (count > 0) { | |||||
if (!next_cluster()) { | |||||
current_cluster = save_cluster; | |||||
return false; | |||||
} | |||||
count--; | |||||
} | |||||
offset = pos; | |||||
return true; | |||||
} | |||||
void File::close() | |||||
{ | |||||
} | |||||
#endif | |||||
#endif |
/* Optimized SD Library for Teensy 3.X | |||||
* Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com | |||||
* | |||||
* Development of this SD library was funded by PJRC.COM, LLC by sales of | |||||
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
* open source software by purchasing genuine Teensy or other PJRC products. | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice, development funding notice, and this permission | |||||
* notice shall be included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
*/ | |||||
#if defined(__arm__) | |||||
#include "SD_t3.h" | |||||
#ifdef USE_TEENSY3_OPTIMIZED_CODE | |||||
uint8_t SDClass::fat_type; | |||||
uint32_t SDClass::fat1_begin_lba; | |||||
uint32_t SDClass::fat2_begin_lba; | |||||
uint32_t SDClass::data_begin_lba; | |||||
uint32_t SDClass::max_cluster; | |||||
uint8_t SDClass::sector2cluster; | |||||
File SDClass::rootDir; | |||||
static uint32_t unaligned_read32_align16(const void *p) | |||||
{ | |||||
#ifdef KINETISK | |||||
return *(const uint32_t *)p; | |||||
#else | |||||
return *(const uint16_t *)p | (*(const uint16_t *)(p+1) << 16); | |||||
#endif | |||||
} | |||||
static uint32_t unaligned_read16_align8(const void *p) | |||||
{ | |||||
#ifdef KINETISK | |||||
return *(const uint16_t *)p; | |||||
#else | |||||
return *(const uint8_t *)p | (*(const uint8_t *)(p+1) << 8); | |||||
#endif | |||||
} | |||||
#define BPB_BytsPerSec 11 // 2 bytes | |||||
#define BPB_SecPerClus 13 // 1 byte | |||||
#define BPB_NumFATs 16 // 1 byte | |||||
#define BPB_RootEntCnt 17 // 1 byte | |||||
#define BPB_TotSec16 19 // 2 bytes | |||||
#define BPB_FATSz16 22 // 2 bytes | |||||
#define BPB_TotSec32 32 // 4 bytes | |||||
#define BPB_FATSz32 36 // 4 bytes | |||||
#define BPB_RootClus 44 // 4 bytes | |||||
bool SDClass::begin(uint8_t csPin) | |||||
{ | |||||
uint8_t status; | |||||
uint32_t cond, hcs, ocr; | |||||
// set up the SPI hardware | |||||
csreg = PIN_TO_BASEREG(csPin); | |||||
csmask = PIN_TO_BITMASK(csPin); | |||||
pinMode(csPin, OUTPUT); | |||||
DIRECT_WRITE_HIGH(csreg, csmask); | |||||
SPI.begin(); | |||||
// send clocks to initialize hardware | |||||
SPI.beginTransaction(SD_SPI_SPEED); | |||||
for (uint8_t i=0; i < 5; i++) SPI.transfer16(0xFFFF); | |||||
// put the card into idle state | |||||
elapsedMillis msec = 0; | |||||
while (1) { | |||||
status = sd_cmd0(); | |||||
//Serial.print("cmd0="); | |||||
//Serial.println(status); | |||||
if (status == 1) break; | |||||
SPI.endTransaction(); | |||||
if (msec > 250) return false; | |||||
SPI.beginTransaction(SD_SPI_SPEED); | |||||
} | |||||
// detect version 1 vs 2 cards | |||||
cond = sd_cmd8(); | |||||
if (cond == 0x80000000) { | |||||
// version 1 card | |||||
card_type = 1; | |||||
hcs = 0; | |||||
} else if (cond == 0x1AA) { | |||||
// version 2 card | |||||
card_type = 2; | |||||
hcs = (1<<30); | |||||
} else { | |||||
SPI.endTransaction(); | |||||
return false; | |||||
} | |||||
//Serial.println(); | |||||
// wait for the card to be ready | |||||
msec = 0; | |||||
while (1) { | |||||
status = sd_acmd41(hcs); | |||||
//Serial.println(); | |||||
if (status == 0) break; | |||||
SPI.endTransaction(); | |||||
if (status > 1) return false; | |||||
if (msec > 1500) return false; | |||||
SPI.beginTransaction(SD_SPI_SPEED); | |||||
} | |||||
// detect high capacity cards | |||||
if (card_type == 2) { | |||||
ocr = sd_cmd58(); | |||||
//Serial.print("ocr ="); | |||||
//Serial.println(ocr, HEX); | |||||
if ((ocr >> 30) == 3) card_type = 3; | |||||
} | |||||
SPI.endTransaction(); | |||||
//Serial.println("init ok"); | |||||
// read the MBR (partition table) | |||||
SDCache s; | |||||
sector_t * mbr = s.read(0); | |||||
//Serial.printf(" mbr sig = %04X\n", mbr->u16[255]); | |||||
if (mbr->u16[255] != 0xAA55) return false; | |||||
uint32_t partition_lba = 0; | |||||
uint32_t index = 446; | |||||
do { | |||||
uint8_t type = mbr->u8[index+4]; | |||||
//Serial.printf(" partition %d is type %d\n", (index-446)/16+1, type); | |||||
if (type == 11 || type == 12) { | |||||
partition_lba = unaligned_read32_align16(mbr->u8 + index + 8); | |||||
//Serial.printf(" partition lba = %d\n", partition_lba); | |||||
break; | |||||
} | |||||
index += 16; | |||||
} while (index < 64); | |||||
s.release(); | |||||
// read the FAT volume ID | |||||
sector_t *vol = s.read(partition_lba); | |||||
if (vol->u16[255] != 0xAA55) return false; | |||||
// BPB_BytsPerSec must be 512 bytes per sector | |||||
if (unaligned_read16_align8(vol->u8 + BPB_BytsPerSec) != 512) return false; | |||||
// BPB_NumFATs must be 2 copies of the file allocation table | |||||
if (vol->u8[BPB_NumFATs] != 2) return false; | |||||
uint32_t reserved_sectors = vol->u16[14/2]; | |||||
if (reserved_sectors == 0) return false; | |||||
//Serial.printf(" reserved_sectors = %d\n", reserved_sectors); | |||||
uint32_t sectors_per_cluster = vol->u8[BPB_SecPerClus]; | |||||
//Serial.printf(" sectors_per_cluster = %d\n", sectors_per_cluster); | |||||
uint32_t s2c = 31 - __builtin_clz(sectors_per_cluster); | |||||
//Serial.printf(" s2c = %d\n", s2c); | |||||
sector2cluster = s2c; | |||||
uint32_t sectors_per_fat = vol->u16[BPB_FATSz16/2]; | |||||
if (sectors_per_fat == 0) sectors_per_fat = vol->u32[BPB_FATSz32/4]; | |||||
//Serial.printf(" sectors_per_fat = %d\n", sectors_per_fat); | |||||
uint32_t root_dir_entries = unaligned_read16_align8(vol->u8 + BPB_RootEntCnt); | |||||
//Serial.printf(" root_dir_entries = %d\n", root_dir_entries); | |||||
uint32_t root_dir_sectors = (root_dir_entries + 31) >> 5; | |||||
//Serial.printf(" root_dir_sectors = %d\n", root_dir_sectors); | |||||
uint32_t total_sectors = unaligned_read16_align8(vol->u8 + BPB_TotSec16); | |||||
if (total_sectors == 0) total_sectors = vol->u32[BPB_TotSec32/4]; | |||||
//Serial.printf(" total_sectors = %d\n", total_sectors); | |||||
fat1_begin_lba = partition_lba + reserved_sectors; | |||||
fat2_begin_lba = fat1_begin_lba + sectors_per_fat; | |||||
data_begin_lba = fat2_begin_lba + sectors_per_fat; | |||||
uint32_t cluster_count = (total_sectors - reserved_sectors | |||||
- root_dir_sectors - (sectors_per_fat << 1)) >> s2c; | |||||
//Serial.printf(" cluster_count = %d\n", cluster_count); | |||||
max_cluster = cluster_count + 1; | |||||
if (cluster_count < 4085) { | |||||
return false; // FAT12 | |||||
} else if (cluster_count < 65525) { | |||||
fat_type = 16; | |||||
rootDir.length = root_dir_entries << 5; | |||||
rootDir.start_cluster = partition_lba + reserved_sectors; | |||||
rootDir.type = FILE_DIR_ROOT16; | |||||
} else { | |||||
fat_type = 32; | |||||
rootDir.length = 0; | |||||
rootDir.start_cluster = vol->u32[BPB_RootClus/4]; | |||||
//Serial.printf(" root cluster = %d\n", rootDir.start_cluster); | |||||
rootDir.type = FILE_DIR; | |||||
} | |||||
rootDir.current_cluster = rootDir.start_cluster; | |||||
rootDir.offset = 0; | |||||
s.release(); | |||||
//Serial.println(sizeof(fatdir_t)); | |||||
return true; | |||||
} | |||||
#endif | |||||
#endif |
/* Optimized SD Library for Teensy 3.X | |||||
* Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com | |||||
* | |||||
* Development of this SD library was funded by PJRC.COM, LLC by sales of | |||||
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
* open source software by purchasing genuine Teensy or other PJRC products. | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice, development funding notice, and this permission | |||||
* notice shall be included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
*/ | |||||
#ifndef _SD_t3_ioreg_h_ | |||||
#define _SD_t3_ioreg_h_ | |||||
// from OneWire.h | |||||
#include "Arduino.h" | |||||
#if defined(__AVR__) | |||||
#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin))) | |||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) | |||||
#define IO_REG_TYPE uint8_t | |||||
#define IO_REG_ASM asm("r30") | |||||
#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0) | |||||
#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) &= ~(mask)) | |||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+1)) |= (mask)) | |||||
#define DIRECT_WRITE_LOW(base, mask) ((*((base)+2)) &= ~(mask)) | |||||
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask)) | |||||
#elif defined(__MK20DX128__) || defined(__MK20DX256__) | |||||
#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) | |||||
#define PIN_TO_BITMASK(pin) (1) | |||||
#define IO_REG_TYPE uint8_t | |||||
#define IO_REG_ASM | |||||
#define DIRECT_READ(base, mask) (*((base)+512)) | |||||
#define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0) | |||||
#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1) | |||||
#define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1) | |||||
#define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1) | |||||
#elif defined(__MKL26Z64__) | |||||
#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) | |||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) | |||||
#define IO_REG_TYPE uint8_t | |||||
#define IO_REG_ASM | |||||
#define DIRECT_READ(base, mask) ((*((base)+16) & (mask)) ? 1 : 0) | |||||
#define DIRECT_MODE_INPUT(base, mask) (*((base)+20) &= ~(mask)) | |||||
#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+20) |= (mask)) | |||||
#define DIRECT_WRITE_LOW(base, mask) (*((base)+8) = (mask)) | |||||
#define DIRECT_WRITE_HIGH(base, mask) (*((base)+4) = (mask)) | |||||
#elif defined(__SAM3X8E__) | |||||
#define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER)) | |||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) | |||||
#define IO_REG_TYPE uint32_t | |||||
#define IO_REG_ASM | |||||
#define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0) | |||||
#define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask)) | |||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask)) | |||||
#define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask)) | |||||
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask)) | |||||
#elif defined(__PIC32MX__) | |||||
#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin))) | |||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) | |||||
#define IO_REG_TYPE uint32_t | |||||
#define IO_REG_ASM | |||||
#define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10 | |||||
#define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08 | |||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04 | |||||
#define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24 | |||||
#define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28 | |||||
#else | |||||
#define PIN_TO_BASEREG(pin) (0) | |||||
#define PIN_TO_BITMASK(pin) (pin) | |||||
#define IO_REG_TYPE unsigned int | |||||
#define IO_REG_ASM | |||||
#define DIRECT_READ(base, pin) digitalRead(pin) | |||||
#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW) | |||||
#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH) | |||||
#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT) | |||||
#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT) | |||||
#endif // CPU types | |||||
#endif |