Any chance of adding SPI.transfer(txBuf, rxBuf, count)?

Status
Not open for further replies.

Bill Greiman

Well-known member
I am again working on a new SD library after almost a year away from micro-controllers. Got hooked on neural networks, spent much money on Xeon servers and 10 Tera-flop GPUs.

I am trying to avoid supporting low-level driver code so I want to use the standard Teensy SPI library.
KurtE has this function which is perfect. It would be great if you added it to the Teensy SPI library.
Code:
  void transfer(const void * buf, void * retbuf, size_t count);
I have found a workaround for the Arduino transfer(buf, count) but don't like it. Here is the difference between KurtE and Arduino.
Code:
  uint8_t receive(uint8_t* buf, size_t n) {
#if USE_KURTE
    SDCARD_SPI.transfer(0, buf, n);
#else
    // can't send junk, SD looks for abort token.
    memset(buf, 0XFF, n);
    SDCARD_SPI.transfer(buf, n); 
#endif
  }

  void send(const uint8_t* buf, size_t n) {
#if USE_KURTE
    SDCARD_SPI.transfer(buf, 0, n);
#else
  // Max transfer is 512 bytes - insure 4-byte aligned.
  uint32_t tmp[128];
  // Can't trash buf - may be cache or user's data.
  memcpy(tmp, buf, n);
  SDCARD_SPI.transfer(tmp, n);
#endif
  }

Use of memcpy() only adds 3 microseconds to send() so I may be too picky about adding a 512 byte tmp buffer.

Why do I want SPI for the built-in SD on Teensy 3.6?

I get lots of requests from people who want to log scientific or engineering data at high rates and are not embedded systems experts. The SDIO controller on Teensy 3.5/3.6 has errata that prevents managing write latency.

My new SD drivers and exFAT implementation allows simple programs to log data reliability at high rates with SPI.

If you use exFAT and preallocate a large file, you can reliably log 5000 records/second (4 ADC values/record) with a simple program like this.

Code:
 data_t buf[BUF_DIM]; // max of 512 bytes

  // skip setup stuff

  while (true) {
    logTime += logInterval;

    // Only 1-2 microseconds jitter in sample time.
    while (((int32_t)micros() - logTime) < 0) {}

    if (i >= BUF_DIM) error("overrun");
    // Read data record
    readData(&buf[i++]);

    if (!sd.card()->isBusy()) {
      // This write will take less than 163 microseconds on Teensy 3.6
      file.write(buf, i*sizeof(data_t));
      i = 0;
    }
  }

Here is typical performance for 512 byte SPI writes with the built-in SD on Teensy 3.6.

KurtE or my old implementation, 200 MB file.
write speed and latency
speed,max,min,avg
KB/Sec,usec,usec,usec
3252.88,163,154,154
3255.00,162,154,154

Only about 10 microseconds variation in write latency!
 
Hi Bill,

Would love to merge this part of my library into the released library.

Also would love to get in the Async stuff, but I believe this part will depend on the new Event stuff Paul is working on.
 
Would love to merge this part of my library into the released library.

Also would love to get in the Async stuff, but I believe this part will depend on the new Event stuff Paul is working on.

Please send a pull request for these, and I'll merge and we can start playing with than on 1.38-beta3...

But I'm not looking to implement transfer16 at this time.
 
I am again working on a new SD library after almost a year away from micro-controllers. Got hooked on neural networks, spent much money on Xeon servers and 10 Tera-flop GPUs.

I tried to figure out if SdFat library supports now also exFAT.
did not look into code, but could not find any hint in examples/documentation.
Did I miss it?
if not, what are plans?
 
Please send a pull request for these, and I'll merge and we can start playing with than on 1.38-beta3...

But I'm not looking to implement transfer16 at this time.

Paul - I took a pass through and merged in the functions to the current Master, which is currently up at: https://github.com/KurtE/SPI/tree/Merge-New_transfer

So far I have tested it out with my Updated Sparkfun Teensy view code, in the branch: https://github.com/KurtE/SparkFun_TeensyView_Arduino_Library/tree/Multiple_SPI_EventResponder

Test APP
Code:
/******************************************************************************
TeensyView_Demo.ino
SFE_TeensyView Library Demo
Jim Lindblom @ SparkFun Electronics
Original Creation Date: October 27, 2014
Modified Febuary 2, 2017
This sketch uses the TeensyView library to draw a 3-D projected
cube, and rotate it along all three axes.
Development environment specifics:
Arduino IDE 1.6.12 w/ Teensyduino 1.31
Arduino IDE 1.8.1 w/ Teensyduino 1.35
TeensyView v1.0
This code is beerware; if you see me (or any other SparkFun employee) at thewa
local, and you've found our code helpful, please buy us a round!
Distributed as-is; no warranty is given.
******************************************************************************/
#include <SPI.h>
#include <TeensyView.h> // Include the SFE_TeensyView library
// /////////////////////////////////
// TeensyView Object Declaration //
// /////////////////////////////////
// #define TLC_SPI1
// #define T36_SPI1
// #define T36_SPI2
// #define DEFAULT_PINS

#ifdef DEFAULT_PINS
#define PIN_RESET 15
#define PIN_DC 5
#define PIN_CS 10
#define PIN_SCK 13
#define PIN_MOSI 11
#endif

#ifdef TLC_SPI1
#define PIN_RESET 15
#define PIN_SCK 20
#define PIN_MOSI 21
#define PIN_DC 4
#define PIN_CS 3
#endif

#ifdef T36_SPI1
#define PIN_RESET 15
#define PIN_SCK 20
#define PIN_MOSI 21
#define PIN_DC 31
#define PIN_CS 32
#endif

#ifdef T36_SPI2
#define PIN_RESET 15
#define PIN_SCK 53
#define PIN_MOSI 52
#define PIN_DC 55
#define PIN_CS 51
#endif

// Kurt's setup

#ifndef PIN_SCK

#if defined(KINETISK)
#define PIN_RESET 15
#define PIN_SCK 13
#define PIN_MOSI 11
#define PIN_DC 21
#define PIN_CS 20
// Setup 2nd one SPI1
#define PIN_RESET1 16
#define PIN_SCK1 32
#define PIN_MOSI1 0
#define PIN_DC1 31
#define PIN_CS1 30

#elif defined(KINETISL)  // Teensy LC
// For multiple need to change CS to not 20 as only valid sck1 on lc
#define PIN_RESET 15
#define PIN_SCK 13
#define PIN_MOSI 11
#define PIN_DC 21
#define PIN_CS 22
// #undef SPI_INTERFACES_COUNT
// #define SPI_INTERFACES_COUNT 1
#define PIN_RESET1 16
#define PIN_SCK1 20
#define PIN_MOSI1 0
#define PIN_DC1 6
#define PIN_CS1 7
#else // AVR... T2 SS(0), SCK(1), MOSI(2), MISO(3)
#define PIN_RESET 5
#define PIN_SCK 13
#define PIN_MOSI 11
#define PIN_DC 4
#define PIN_CS 0
#endif

// Pins on connector on Beta T3.6 board (3.3, GND)(48, 47)(57 56) (51 52) (53 55)
#define PIN_RESET2 48
// #define PIN_MISO2 51
#define PIN_MOSI2 52
#define PIN_SCK2 53
#define PIN_DC2 55
#define PIN_CS2 56
#endif

TeensyView32 oled(PIN_RESET, PIN_DC, PIN_CS, PIN_SCK, PIN_MOSI);

#if SPI_INTERFACES_COUNT > 1
TeensyView64 oled1(PIN_RESET1, PIN_DC1, PIN_CS1, PIN_SCK1, PIN_MOSI1);

    #if SPI_INTERFACES_COUNT > 2
TeensyView32 oled2(PIN_RESET2, PIN_DC2, PIN_CS2, PIN_SCK2, PIN_MOSI2);
    #endif

#endif

#ifdef SPI_HAS_TRANSFER_ASYNC
#define DO_ASYNC true
#define DISPLAYFUNC displayAsync
#else
#define DO_ASYNC false
#define DISPLAYFUNC display
#endif


TeensyView * oleds[] =
{
#if 0
    & oled,
    & oled1
#else  
    & oled

#if SPI_INTERFACES_COUNT > 1
    , & oled1

    #if SPI_INTERFACES_COUNT > 2
    , & oled2
    #endif
#endif
#endif

};

uint8_t oled_which_test[] =
{
    0, 0, 0
};

uint16_t test_iterations_left[] =
{
    0xffff, 0xffff, 0xffff
};

uint32_t last_test_start_time[] =
{
    0, 0, 0
};

uint32_t next_test_start_time[] =
{
    0, 0, 0
};

extern void testRects(TeensyView * _oled, bool draw_async, uint16_t & iterations_left);
extern void testCircles(TeensyView * _oled, bool draw_async, uint16_t & iterations_left);
extern void TestpixelsAsync(TeensyView * _oled, uint16_t & iterations_left);
extern void TestFillRects(TeensyView * _oled, uint16_t & iterations_left);
extern void testdrawline(TeensyView * _oled, uint16_t & iterations_left, uint32_t & next_test_start_time);
extern void testscrolltext(TeensyView * _oled, uint16_t & iterations_left, uint32_t & next_test_start_time);
void setup()
{
    pinMode(2, OUTPUT);
    digitalWrite(2, LOW);
    pinMode(3, OUTPUT);
    digitalWrite(3, LOW);
    while (!Serial && millis() < 3000);
    Serial.begin(38400);
    oled.begin(); // Initialize the OLED
    oled.display(); // Display what's in the buffer (splashscreen)

#if SPI_INTERFACES_COUNT > 1
    oled1.begin(); // Initialize the OLED
    oled1.display(); // Display what's in the buffer (splashscreen)''
    Serial.println("oled1 displayed");
#endif

#if SPI_INTERFACES_COUNT > 2
    oled2.begin(); // Initialize the OLED
    oled2.display(); // Display what's in the buffer (splashscreen)''
    Serial.println("oled2 displayed");
#endif

    delay(1000); // Delay 1000 ms
    oled.clear(HARDWARE_MEM); // Clear the buffer.

#if SPI_INTERFACES_COUNT > 1
    oled1.clear(HARDWARE_MEM); // Clear the buffer.
#endif

#if SPI_INTERFACES_COUNT > 2
    oled2.clear(HARDWARE_MEM);
#endif
#ifdef A1
    randomSeed(analogRead(A0) + analogRead(A1));
#else
   randomSeed(analogRead(A0) + (analogRead(A0)<<1));
#endif    
}

void loop()
{
    // Lets see which of our displays is ready to display something different
    for (uint8_t i = 0; i < sizeof(oleds) /sizeof(oleds[0]); i++)
    {
#ifdef SPI_HAS_TRANSFER_ASYNC
        if ((millis() > next_test_start_time[i]) && !oleds[i] -> displayAsyncActive())
#else
        if ((millis() > next_test_start_time[i]))
#endif
        {
            last_test_start_time[i] = millis();
            switch (oled_which_test[i])
            {
                case 0:
                    testRects(oleds[i], DO_ASYNC, test_iterations_left[i]);
                    break;
                case 1:
                    testRects(oleds[i], DO_ASYNC, test_iterations_left[i]);
                    break;
                case 2:
                    TestpixelsAsync(oleds[i], test_iterations_left[i]);
                    break;
                case 3:
                    TestFillRects(oleds[i], test_iterations_left[i]);
                    break;
                case 4:
                    testdrawline(oleds[i], test_iterations_left[i], next_test_start_time[i]);
                    break;
                case 5:
                    testscrolltext(oleds[i], test_iterations_left[i], next_test_start_time[i]);
                    break;
            }
            if (test_iterations_left[i] == 0)
            {
                oled_which_test[i] ++;
                if (oled_which_test[i] > 5)
                    oled_which_test[i] = 0;
                test_iterations_left[i] = 0xffff; // mark it special for first call
                next_test_start_time[i] = millis() + 100;
            }
        }
        else
            if ((millis() - last_test_start_time[i]) > 2500)
            {
                Serial.printf("Oled %d hung test: %d iter: %d\n ", i, oled_which_test[i], test_iterations_left[i]);
                printDebugInfo(oleds[i]);
                last_test_start_time[i] = millis();
            }
    }
}

void printDebugInfo(TeensyView * _oled)
{

#ifdef KINETISK

  #ifdef SPI_DEBUG_ASYNC_T3X
    extern void dumpDMA_TCD(const char * psz, DMABaseClass * dmabc);
    dumpDMA_TCD("TX:", _oled -> _spi -> _dmaTX);
    dumpDMA_TCD("RX:", _oled -> _spi -> _dmaRX);
  #endif  
#else

        #ifdef SPI_DEBUG_ASYNC_LC
    extern void dumpDMA_CFG(const char * sz, DMABaseClass * dmabc);
    dumpDMA_CFG("TX:", _oled -> _spi -> _dmaTX);
    dumpDMA_CFG("RX:", _oled -> _spi -> _dmaRX);
        #endif

#endif

}

void testRects(TeensyView * _oled, bool draw_async, uint16_t & iterations_left)
{
    int n, i, i2;
    int cx = _oled -> getLCDWidth() / 2;
    int cy = _oled -> getLCDHeight() / 2;
    _oled -> clear(PAGE);
    n = min(_oled -> getLCDWidth(), _oled -> getLCDHeight());
    for (i = 2; i < n; i += 6)
    {
        i2 = i / 2;
        _oled -> rect(cx - i2, cy - i2, i, i);
    }
    if (draw_async)
    {
        _oled -> DISPLAYFUNC();
    }
    else
    {
        _oled -> display();
    }
    iterations_left = 0;
}

void testCircles(TeensyView * _oled, bool draw_async, uint16_t & iterations_left)
{
    uint16_t radius = 10;
    int x, y, r2 = radius * 2,
    w = _oled -> getLCDWidth() + radius,
    h = _oled -> getLCDHeight() + radius;
    _oled -> clear(PAGE);
    for (x = 0; x < w; x += r2)
    {
        for (y = 0; y < h; y += r2)
        {
            _oled -> circle(x, y, radius);
        }
    }
    if (draw_async)
    {
        _oled -> DISPLAYFUNC();
    }
    else
    {
        _oled -> display();
    }
    iterations_left = 0;
}

void TestpixelsAsync(TeensyView * _oled, uint16_t & iterations_left)
{
    if (iterations_left == 0xffff)
    {
        _oled -> clear(PAGE);
        iterations_left = 1024;
    }
    _oled -> pixel(random(_oled -> getLCDWidth()), random(_oled -> getLCDHeight()));
    _oled -> DISPLAYFUNC();
    iterations_left--;
}

void TestFillRects(TeensyView * _oled, uint16_t & iterations_left)
{
    if (iterations_left == 0xffff)
    {
        _oled -> clear(PAGE);
        iterations_left = 0; // Not really, but makes it simple as we will update
    }
    _oled -> rectFill(iterations_left, iterations_left,
    _oled -> getLCDWidth() - iterations_left * 2, _oled -> getLCDHeight() - iterations_left * 2,(iterations_left & 1)? 0:1, NORM);
    _oled -> DISPLAYFUNC();
    iterations_left += 3;
    if (iterations_left >= _oled -> getLCDHeight() /2)
    {
        iterations_left = 0; // we are done.
    }
}

void testdrawline(TeensyView * _oled, uint16_t & iterations_left, uint32_t & next_test_start_time)
{
    // Serial.printf("testDrawline %x %x\n", _oled, iterations_left);
    if (iterations_left == 0xffff)
    {
        _oled -> clear(PAGE);
        iterations_left = 0; // Not really, but makes it simple as we will update
    }
    uint8_t line_test = iterations_left >> 8;
    uint8_t i = iterations_left & 0xff;
    switch (line_test)
    {
        case 0:
            _oled -> line(0, 0, i, _oled -> getLCDHeight() - 1);
            _oled -> DISPLAYFUNC();
            i += 4;
            if (i >= _oled -> getLCDWidth())
            {
                i = 0;
                line_test++;
            }
            break;
        case 1:
            _oled -> line(0, 0, _oled -> getLCDWidth() - 1, i);
            _oled -> DISPLAYFUNC();
            i += 4;
            if (i >= _oled -> getLCDHeight())
            {
                i = 0;
                line_test++;
                next_test_start_time = millis() + 250;
            }
        case 2:
            if (i == 0)
            {
                _oled -> clear(PAGE);
            }
            _oled -> line(0, _oled -> getLCDHeight() - 1, i, 0);
            _oled -> DISPLAYFUNC();
            i += 4;
            if (i >= _oled -> getLCDWidth())
            {
                i = 0;
                line_test++;
            }
            break;
        case 3:
            _oled -> line(0, _oled -> getLCDHeight() - 1, _oled -> getLCDWidth() - 1, i);
            _oled -> DISPLAYFUNC();
            i += 4;
            if (i >= _oled -> getLCDHeight())
            {
                i = 0;
                line_test++;
                next_test_start_time = millis() + 250;
            }
            break;
        case 4:
            if (i == 0)
            {
                _oled -> clear(PAGE);
            }
            _oled -> line(_oled -> getLCDWidth() - 1, _oled -> getLCDHeight() - 1, i, 0);
            _oled -> DISPLAYFUNC();
            i += 4;
            if (i >= _oled -> getLCDWidth())
            {
                i = 0;
                line_test++;
            }
            break;
        case 5:
            _oled -> line(_oled -> getLCDWidth() - 1, _oled -> getLCDHeight() - 1, 0, i);
            _oled -> DISPLAYFUNC();
            i += 4;
            if (i >= _oled -> getLCDHeight())
            {
                i = 0;
                line_test++;
                next_test_start_time = millis() + 250;
            }
            break;
        case 6:
            if (i == 0)
            {
                _oled -> clear(PAGE);
            }
            _oled -> line(_oled -> getLCDWidth() - 1, 0, 0, i);
            _oled -> DISPLAYFUNC();
            i += 4;
            if (i >= _oled -> getLCDHeight())
            {
                i = 0;
                line_test++;
            }
            break;
        case 7:
            _oled -> line(_oled -> getLCDWidth() - 1, 0, i, _oled -> getLCDHeight() - 1);
            _oled -> DISPLAYFUNC();
            i += 4;
            if (i >= _oled -> getLCDWidth())
            {
                i = 0;
                line_test = 0; // Say we are done
            }
    }
    iterations_left = (line_test << 8) | i;
}

void testscrolltext(TeensyView * _oled, uint16_t & iterations_left, uint32_t & next_test_start_time)
{
    if (iterations_left == 0xffff)
    {
        int middleX = _oled -> getLCDWidth() / 2;
        int middleY = _oled -> getLCDHeight() / 2;
        _oled -> clear(PAGE);
        _oled -> setFontType(1);
        // Try to set the cursor in the middle of the screen
        _oled -> setCursor(middleX - (_oled -> getFontWidth() * (6 / 2)),
        middleY - (_oled -> getFontWidth() / 2));
        // Print the title:
        _oled -> print("Scroll");
        _oled -> DISPLAYFUNC();
        iterations_left = 1; // Not really, but makes it simple as we will update
        return;
    }
    switch (iterations_left)
    {
        case 1:
            _oled -> scrollRight(0x00, 0x0f, true);
            next_test_start_time = millis() + 2000;
            break;
        case 2:
            _oled -> scrollStop();
            next_test_start_time = millis() + 1000;
            break;
        case 3:
            _oled -> scrollLeft(0x00, 0x0f, true);
            next_test_start_time = millis() + 2000;
            break;
        case 4:
            _oled -> scrollStop();
            next_test_start_time = millis() + 1000;
            break;
        case 5:
            _oled -> scrollVertRight(0x00, 0x07, true);
            next_test_start_time = millis() + 2000;
            break;
        case 6:
            _oled -> scrollVertLeft(0x00, 0x07, true);
            next_test_start_time = millis() + 2000;
            break;
        case 7:
            _oled -> scrollStop();
            next_test_start_time = millis() + 50;
            break;
    }
    if (++iterations_left > 7)
        iterations_left = 0;
}
I ran it on T-LC with 2 displays
T3.2 - 1 display
T3.5 - 3 displays
T3.6 - 3 displays

I have not tried it yet with T3.0 - I have 1 but have not soldered headers on it yet. Nor with T2.0...
On T2.0 I added the SPI.transfer(buf, retbuf, cnt), but not the Async version as no Event Responder...
 
Glad to see transfer(txBuf, rxBuf, count)! I will get rid of my old Teensy SPI driver.

I tried to figure out if SdFat library supports now also exFAT

I finally decided to write a new library. Trying to add exFAT to existing FAT code as in FatFs is a killer.

The new library, SdFs supports both FAT and exFAT but more like the way Linux supports many filesystems. I restructured/rewrote the SdFat class, wrote a new SdExFat class and use both in a SdFs class. I change the chip select parameter to an SD interface parameter so there are not separate classes for shared SPI, dedicated SPI, DMA SDIO, FIFO SDIO.

I will post a beta of SdFs in the near future. I am finishing new ChibiOS/RT and ChibiOS/nil RTOS ports.

Here is the simplest possible Teensy 3.6 example.

Code:
#include "SdFs.h"

// both FAT and exFAT
SdFs sd;

// FsFile for both FAT and exFAT files. 
FsFile file;

void setup() {
  Serial.begin(9600);
  
  // Can support Teensy 3.6 in SPI mode with SpiInterface(csPin, options, spiPort)

  if (!sd.begin(SdioInterface(TEENSY_FIFO))) {
    Serial.println("sd.begin failed");
    while (true);
  }
  // open with 8-bit path can have UTF-16
  file = sd.open("my file.txt", FILE_WRITE);
  if (!file) {
    Serial.println("open failed");
    while (true);    
  }
  file.println("test string");
  file.close();
  Serial.println("Done");
}
void loop() {}
Size on Teensy 3.6:
Sketch uses 31992 bytes (3%) of program storage space. Maximum is 1048576 bytes.
Global variables use 6312 bytes (2%) of dynamic memory, leaving 255832 bytes for local variables. Maximum is 262144 bytes.

It will run on Uno using sd.begin(SpiInterface(csPin)).

Size on Uno:
Sketch uses 19618 bytes (60%) of program storage space. Maximum is 32256 bytes.
Global variables use 938 bytes (45%) of dynamic memory, leaving 1110 bytes for local variables. Maximum is 2048 bytes.

Size on Uno with exFAT only class SdExFat :
Sketch uses 12682 bytes (39%) of program storage space. Maximum is 32256 bytes.
Global variables use 939 bytes (45%) of dynamic memory, leaving 1109 bytes for local variables. Maximum is 2048 bytes.
 
Last edited:
Some time ago I recall you were talking about a generic FAT filesystem library. Is that still something you're considering?

Long-term, I believe we're going to need this on Teensy and many other Arduino compatible boards. At the very least, USBHost's MSC driver will need this. Right now, I believe Andrew has reinvented much of the FAT wheel. I'm looking at doing something similar for the USBHost_t36 library.

Early in 2016 I also put some wheel reinvention work into Teensy's SD lib. Currently it's incomplete and only reads but can't write. It does have a interrupt safe caching layer (using SPI.beginTransaction for locking), which allows the audio library to use it while the main program also accesses files and directories.

The other thing we're really going to need long-term is a File base class. Looks like you have FsFile in the new code. Arduino's Bridge library has File under a BridgeLib namespace. SerialFlash has SerialFlashFile. We need a base class which can be used with libraries for graphics, fonts, data logging, maybe even networked filesystems, so they can access files from SD, SDFat, Bridge, USBHost, MTP or other sources.

Sorry if this just makes everything more complicated... I've been thinking quite a lot lately about what Arduino really needs for its future.
 
Some time ago I recall you were talking about a generic FAT filesystem library. Is that still something you're considering?

The new base libraries in SdFs are generic. FatLib and ExFatLib expect an abstract block interface. The Interface still has 512 byte sectors but I am making sector size symbolic.

I tested the idea with a USB shield and with Arduino ADK for USB sticks and USB hard drives. I also use an early version of the new code on the ChibiOS RTOS with it's SD drivers.

I include the option of read/write single block and read/write multiple blocks since these are supported in different ways on various flash devices. There may be an efficient way to randomly read/write a single block.
Code:
class BlockDeviceInterface {
 public:
  /**
   * Read a 512 byte sector.
   *
   * \param[in] sector Logical sector to be read.
   * \param[out] dst Pointer to the location that will receive the data.
   * \return The value true is returned for success and
   * the value false is returned for failure.
   */  
  virtual bool readSector(uint32_t sector, uint8_t* dst) = 0;

  /**
   * Read multiple 512 byte sectors.
   *
   * \param[in] sector Logical sector to be read.
   * \param[in] ns Number of sectors to be read.
   * \param[out] dst Pointer to the location that will receive the data.
   * \return The value true is returned for success and
   * the value false is returned for failure.
   */  
  virtual bool readSectors(uint32_t sector, uint8_t* dst, size_t ns) = 0; 
  
  /** \return device size in sectors. */
  virtual uint32_t sectorCount() = 0;

  /** End multi-sector transfer and go to idle state.
   * \return The value true is returned for success and
   * the value false is returned for failure.
   */
  virtual bool syncDevice() = 0;
  
  /**
   * Writes a 512 byte sector.
   *
   * \param[in] sector Logical sector to be written.
   * \param[in] src Pointer to the location of the data to be written.
   * \return The value true is returned for success and
   * the value false is returned for failure.
   */    
  virtual bool writeSector(uint32_t sector, const uint8_t* src) = 0;

  /**
   * Write multiple 512 byte sectors.
   *
   * \param[in] sector Logical sector to be written.
   * \param[in] ns Number of sectors to be written.
   * \param[in] src Pointer to the location of the data to be written.
   * \return The value true is returned for success and
   * the value false is returned for failure.
   */    
  virtual bool writeSectors(uint32_t sector, const uint8_t* src, size_t ns) = 0;
};
Each instance of a volume class has it's own cache so you can have many volumes on various devices.

The FAT class may have a cache for the FAT and one for data. FAT access is high overhead since you must maintain two copies. exFAT has a single copy of the FAT and on a clean volume files are contiguous so the FAT is not used.
 
Last edited:
Paul - I took a pass through and merged in the functions to the current Master, which is currently up at: https://github.com/KurtE/SPI/tree/Merge-New_transfer
...
I ran it on T-LC with 2 displays
T3.2 - 1 display
T3.5 - 3 displays
T3.6 - 3 displays

I have not tried it yet with T3.0 - I have 1 but have not soldered headers on it yet. Nor with T2.0...
On T2.0 I added the SPI.transfer(buf, retbuf, cnt), but not the Async version as no Event Responder...
Soldered headers on my 1 T3.0 (close out sale) and the Teensyview program runs on it.

T2.0 - Fixed compile error, a data member was under #ifdef that should not have been. Also updated my Teensyview library to work without Async and then was able to run the Teensyview demo on T2.0 - Both libraries updated... Also updated test program to have SPI 2.0 pins used... Have not tested on 2.0+ as don't have one.
 
Status
Not open for further replies.
Back
Top