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