@@ -13,6 +13,7 @@ | |||
*/ | |||
#include <SD.h> | |||
#ifndef __SD_t3_H__ | |||
/* for debugging file open/close leaks | |||
uint8_t nfilecount=0; | |||
@@ -147,4 +148,4 @@ File::operator bool() { | |||
return _file->isOpen(); | |||
return false; | |||
} | |||
#endif |
@@ -51,6 +51,7 @@ | |||
*/ | |||
#include "SD.h" | |||
#ifndef __SD_t3_H__ | |||
// Used by `getNextPathComponent` | |||
#define MAX_COMPONENT_LEN 12 // What is max length? | |||
@@ -614,3 +615,4 @@ void File::rewindDirectory(void) { | |||
} | |||
SDClass SD; | |||
#endif |
@@ -1,3 +1,6 @@ | |||
#if defined(__arm__) | |||
#include "SD_t3.h" | |||
#endif | |||
/* | |||
SD - a slightly more friendly wrapper for sdfatlib |
@@ -0,0 +1,240 @@ | |||
/* 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 |
@@ -0,0 +1,207 @@ | |||
/* 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 |
@@ -0,0 +1,171 @@ | |||
/* 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 |
@@ -0,0 +1,148 @@ | |||
/* 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 |
@@ -0,0 +1,63 @@ | |||
/* 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 |
@@ -0,0 +1,193 @@ | |||
/* 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 |
@@ -0,0 +1,203 @@ | |||
/* 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 |
@@ -0,0 +1,102 @@ | |||
/* 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 |