Browse Source

Add Teensy 3.x optimized code

main
PaulStoffregen 9 years ago
parent
commit
b91b00957c
11 changed files with 1334 additions and 1 deletions
  1. +2
    -1
      File.cpp
  2. +2
    -0
      SD.cpp
  3. +3
    -0
      SD.h
  4. +240
    -0
      SD_t3.h
  5. +207
    -0
      cache_t3.cpp
  6. +171
    -0
      card_t3.cpp
  7. +148
    -0
      dir_t3.cpp
  8. +63
    -0
      fat_t3.cpp
  9. +193
    -0
      file_t3.cpp
  10. +203
    -0
      init_t3.cpp
  11. +102
    -0
      utility/ioreg.h

+ 2
- 1
File.cpp View File

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

+ 2
- 0
SD.cpp View File

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

+ 3
- 0
SD.h View File

@@ -1,3 +1,6 @@
#if defined(__arm__)
#include "SD_t3.h"
#endif
/*

SD - a slightly more friendly wrapper for sdfatlib

+ 240
- 0
SD_t3.h View File

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

+ 207
- 0
cache_t3.cpp View File

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

+ 171
- 0
card_t3.cpp View File

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

+ 148
- 0
dir_t3.cpp View File

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

+ 63
- 0
fat_t3.cpp View File

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

+ 193
- 0
file_t3.cpp View File

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

+ 203
- 0
init_t3.cpp View File

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

+ 102
- 0
utility/ioreg.h View File

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

Loading…
Cancel
Save