| @@ -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 | |||