Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 10 of 10

Thread: SD card interface: Teensy 4.1 vs Teensy 4.0 + Audio adapter

  1. #1
    Junior Member
    Join Date
    Jul 2020
    Location
    The Road
    Posts
    7

    SD card interface: Teensy 4.1 vs Teensy 4.0 + Audio adapter

    Hello,

    I'm looking to build an audio project that reads and writes audio to/from an SD card, and requires 8 analog inputs (pots to control volume etc) as well as audio I/O. As someone diving into this world for the first time, I initially chose the Teensy 4.1 because of its built in SD card interface and plentiful I/O; I would also get the compatible Audio Adapter for it.

    But then I noticed that the Audio Adapter has an SD card slot of its own! So my questions are:

    • Are there any disadvantages or differences to accessing the SD using a Teensy 4.0 with the Audio Adapter's SD card slot, vs using a 4.1's integrated SD slot?
    • Would I still have enough (8) analog inputs available with the 4.0, after the audio adapter is connected?
    • (out of curiosity) if I connected a Teensy 4.1 to an audio adapter, could I use both SD card slots at the same time?


    Thank you,
    A music nerd

  2. #2
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,725
    Quote Originally Posted by even_rats View Post
    • Are there any disadvantages or differences to accessing the SD using a Teensy 4.0 with the Audio Adapter's SD card slot, vs using a 4.1's integrated SD slot?
    • Would I still have enough (8) analog inputs available with the 4.0, after the audio adapter is connected?
    • (out of curiosity) if I connected a Teensy 4.1 to an audio adapter, could I use both SD card slots at the same time?
    Access to T4.1 Sd card is faster (it uses 4-bit parallel SDIO protocol, while AudioAdapter user 1-bit SPI prorocol)
    Analog input on Teensy are independent from Audio I/O of Audio Adapter
    yes you can use both SD Cards (T4.1 and AudioAdapter). only access speed is different (see above)

  3. #3
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,837
    I've tested both, and for reading a single file:
    • Teensy 4.0 using audio shield: 0.9 Mbyte/second;
    • Teensy 4.0 using SD card via soldered pins: 1.2 Mbyte/second;
    • Teensy 4.1 using built-in micro SD card reader: 1.2 Mbyte/second;
    • Teensy 3.2, 3.5, and 3.6 have roughly the same speed using the audio shield as the Teensy 4.0; (and)
    • Teensy 3.5 and 3.6 have roughly the same speed using the built-in micro SD card reader.


    This is using the standard '<SD.h>' library. I haven't tested other libraries.

  4. #4
    Junior Member
    Join Date
    Jul 2020
    Location
    The Road
    Posts
    7
    Very helpful replies. Good to know the read speed in practice. Thanks!

    Quote Originally Posted by MichaelMeissner View Post
    • Teensy 4.1 using built-in micro SD card reader: 1.2 Mbyte/second;
    Any idea if the write speeds would be similar?

  5. #5
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,837
    Quote Originally Posted by even_rats View Post
    Very helpful replies. Good to know the read speed in practice. Thanks!


    Any idea if the write speeds would be similar?
    I didn't test it. My main interest is in having pre-recorded sounds and playing them. The test I used was the example Audio -> HardwareTesting -> SDCardTest, with the appropriate modifications for audio and built-in sd cards.

    I was disappointed that the built-in sd reader wasn't faster.

    I am hoping that when Paul's rewrite of the file system support goes into TeensyDunio 1.54 will allow for easier support of flash memory (either on the audio shield, or soldered onto the Teensy 4.1).

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,516
    Quote Originally Posted by MichaelMeissner View Post
    I am hoping that when Paul's rewrite of the file system support goes into TeensyDunio 1.54 will allow for easier support of flash memory (either on the audio shield, or soldered onto the Teensy 4.1).
    In the short term, I'm aiming for a much lower goal... just to make the File class from SD into a base class which can be shared among libraries.

    Years ago I started a redesign of the SD library. It only supports SPI and only on Teensy 3.x, and so far only offers read-only access to the card. Eventually I will revisit that, or perhaps merge Bill's work on SdFat. But that is a much longer term goal. It's a huge project. It also didn't make as much sense on Teensy 3.0 to 3.2, where we have much less RAM for caching. SD cards have high command latency, so the trick to good performance is many-sector access and lots of caching.

  7. #7
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,837
    Quote Originally Posted by PaulStoffregen View Post
    In the short term, I'm aiming for a much lower goal... just to make the File class from SD into a base class which can be shared among libraries.
    As a coworker of mine used to say before he retired "It's a journey, not an event".

    If would be nice if we could use a common FILE base class among the filesystems, and for WAV/RAW/MP3/etc. files. But I realize it likely will take some amount of time to get there.

    And of course, real life has a tendency to get in the way of things. I need to get back to programming Teensies.

  8. #8
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,725
    Quote Originally Posted by PaulStoffregen View Post
    In the short term, I'm aiming for a much lower goal... just to make the File class from SD into a base class which can be shared among libraries.
    Would it make sense, to define right now a virtual base class that implements a user interface? Next we could try to think about the different implementations.
    Not sure if the old Arduino interface is still the right way. what about Unix style API?
    Anyhow, someone must IMHO indicate the road, we will then run along.

  9. #9
    Senior Member
    Join Date
    Sep 2015
    Posts
    108
    I'm moving back around to this on my current project and would be happy to contribute however I can as soon as there is some sort of marching orders. My personal goals are some level of compatibility between SD and NAND storage and a non-blocking read call.

  10. #10
    Senior Member
    Join Date
    Feb 2018
    Location
    Corvallis, OR
    Posts
    189
    Quote Originally Posted by MichaelMeissner View Post
    I've tested both, and for reading a single file:
    • Teensy 4.0 using audio shield: 0.9 Mbyte/second;
    • Teensy 4.0 using SD card via soldered pins: 1.2 Mbyte/second;
    • Teensy 4.1 using built-in micro SD card reader: 1.2 Mbyte/second;
    • Teensy 3.2, 3.5, and 3.6 have roughly the same speed using the audio shield as the Teensy 4.0; (and)
    • Teensy 3.5 and 3.6 have roughly the same speed using the built-in micro SD card reader.


    This is using the standard '<SD.h>' library. I haven't tested other libraries.
    If read/write performance is an issue, you should be using Bill Greiman's SDFat 2.0b and the built-in SD card on the T3.6 or T4.1. I've found SDFat to be very stable and with MUCH higher performance when you read and write in blocks of 4KB. I think the speed increase is probably due to SDFat's use of multi-sector reads and writes.

    Code:
    SdFs  32GB Sandisk ExFat   T3.6 180MHz
    Write                       15.645 MB/Sec
    Random read 4K blocks        4.096 MB/Sec
    Sequential read             19.527 MB/Sec
    
    SdFs  32GB Sandisk FAT32   T3.6 180MHz
    Write                       13.224 MB/Sec
    Random Read 4K blocks        4.410 MB/Sec
    Sequential read             19.523 MB/Sec
    
    
    
    SdFs  32GB Sandisk ExFat   T4.1 600MHz
    Write                       17.494 MB/Sec
    Random read 4K blocks        4.681 MB/Sec
    Sequential read             22.864 MB/Sec
    
    SdFs  32GB Sandisk FAT32    T4.1 600MHz
    Write                       17.172 MB/Sec
    Random Read                  5.079 MB/Sec
    Sequential read             22.859 MB/Sec
    Note that the speeds don't increase much with the T4.1, despite its much higher CPU clock speed. That indicates that the speed limit is probably imposed by either the SD card or the SDIO hardware. I have found that there are significant differences between SD cards, so you probably need to do your own tests with the cards you plan to use.

    Here's the benchmark program I used to collect the data:
    Code:
    /*********************************************************************
     * Benchmark test for SD FAT32 sequential and random access
     * M. Borgerson 8/24/2020
     ********************************************************************/
    #include "SdFat.h"
    #include "sdios.h"
    #include <TimeLib.h>
    
    // NOTE:  I used SdFs because it will use either FAT32 or ExFAT, depending on the format of the disk
    SdFs sd;
    FsFile BenchFile;
    
    
    #define SD_CONFIG SdioConfig(FIFO_SDIO)
    
    // SDCARD_SS_PIN is defined for the built-in SD on some boards.
    #ifndef SDCARD_SS_PIN
    const uint8_t SD_CS_PIN = SS;
    #else  // SDCARD_SS_PIN
    // Assume built-in SD is used.
    const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
    #endif  // SDCARD_SS_PIN
    
    
    const char compileTime [] = "SdFs Benchmark  Compiled on " __DATE__ " " __TIME__;
    
    /*****************************************************************************
       Read the Teensy RTC and return a time_t (Unix Seconds) value
    
     ******************************************************************************/
    time_t getTeensy3Time() {
      return Teensy3Clock.get();
    }
    
    
    void setup() {
      // put your setup code here, to run once:
    
      while (!Serial) {}
      Serial.begin(9600);
      delay(1000);
    
      Serial.println(compileTime); 
      if (!sd.begin(SD_CONFIG)) {
        Serial.println("\nSDIO Card initialization failed.\n");
      } else  Serial.println("SDIO initialization done.");
    
      if (sd.fatType() == FAT_TYPE_EXFAT) {
        Serial.println("Type is exFAT");
      } else {
        Serial.printf("Type is FAT%d\n", int16_t(sd.fatType()));
      }
      // set date time callback function  so file gets a good date
      SdFile::dateTimeCallback(dateTime);
      setSyncProvider(getTeensy3Time);
    }
    
    char fname[] = "btest.dat";
    void loop() {
      // put your main code here, to run repeatedly:
      char ch;
      if (Serial.available()) {
        ch = Serial.read();
        if (ch == 'w')  WriteBenchFile(fname);
        if (ch == 's')  SeqReadBenchFile(fname);
        if (ch == 'r')  RandReadBenchFile(fname);
        if (ch == 'd')  sd.ls(LS_SIZE | LS_DATE | LS_R);
      }
    }
    
    #define RBUFFSIZE 4096
    // Write a 256MB  contiguous file for benchmark testing
    //  
    // data in the file will be random data from stack area
    void WriteBenchFile(char *filename) {
      uint64_t alloclength;
      uint32_t i, num;
      FsFile benchFile;
      float seconds;
      uint32_t startmilli, dmilli, blockmax, mbytes;
      unsigned char benchbuff[RBUFFSIZE];
    
      Serial.printf("\n\nWriting 256MB Benchmark file.\n");
      // Open the file
      if (!benchFile.open(filename,  O_RDWR | O_CREAT | O_TRUNC)) {
        Serial.printf("Unable to open <%s> for writing.", filename);
        return;
      }
    
    
      // now write the data in blocks of RBUFFSIZE --   65536 blocks
      // send out a message every 10MB or 256 blocks
      blockmax = 65536;
      mbytes = 0;
      startmilli = millis();
      for(i=0; i<blockmax; i++){
        benchFile.write(&benchbuff, 4096);
        if((i%2560) == 0){
          Serial.printf("%lu MBytes\n",mbytes);
          mbytes+= 10;
        }
      }
      dmilli = millis()-startmilli;
    
      benchFile.close();
      seconds = (float)dmilli/1000.0;
      Serial.printf("\nWriting 256MBytes took %4.2f seconds", (float)dmilli/1000.0);
      Serial.printf(" For an average rate of %6.3f Mbytes/second\n", (256)/seconds);
    }
    
    
    // Read 40 MBytes from benchFile and keep track of max and average read time
    
    #define BUFFSTOREAD 10240   // 40MByte read
    void SeqReadBenchFile(const char *filename) {
      uint16_t idx, numread;
      FsFile benchFile;
      uint8_t rbuffer[RBUFFSIZE];
      uint32_t startmicro, dmicro, maxmicro, gt500us;
      float microsum;
    
      if (!benchFile.open(filename, O_READ)) {
        Serial.printf("\nCould not open <%s> for reading.", filename);
        return;
      }
      startmicro = micros();  // save starting time
      Serial.printf("\n\nReading %lu sequential buffers of %u bytes\n",BUFFSTOREAD, RBUFFSIZE);
      maxmicro = 0; 
      microsum = 0.0;
      gt500us = 0;
      for(idx = 0; idx<BUFFSTOREAD; idx++){
        startmicro = micros();
        numread = benchFile.read(&rbuffer, RBUFFSIZE);
        dmicro = micros()-startmicro;
        if(dmicro > maxmicro) maxmicro = dmicro;
        if(dmicro > 500) gt500us++;
        microsum += dmicro;
    
      }
      Serial.printf("Buffer read times: average = %4.2f usec   Maximum = %lu usec    >500uSec: %lu\n",
                                  microsum/BUFFSTOREAD, maxmicro, gt500us);
      Serial.printf("Average Read Rate = %6.3f MBytes/second)", (float)(10240 *4096)/(float)microsum);
      
      benchFile.close();
      Serial.println();
    
    }
    
    void RandReadBenchFile(const char *filename) {
    
      uint16_t idx, numread;
      FsFile benchFile;
      uint8_t rbuffer[RBUFFSIZE];
      uint32_t startmicro, dmicro, maxmicro, gt500us, fpos;
      float microsum;
    
      if (!benchFile.open(filename, O_READ)) {
        Serial.printf("\nCould not open <%s> for reading.", filename);
        return;
      }
      startmicro = micros();  // save starting time
      Serial.printf("\n\nReading %lu random buffers of %u bytes\n",BUFFSTOREAD, RBUFFSIZE);
      maxmicro = 0; 
      microsum = 0.0;
      gt500us = 0;
      for(idx = 0; idx<BUFFSTOREAD; idx++){
        startmicro = micros();
        fpos = random(0,5000)*RBUFFSIZE;
        benchFile.seek(fpos);
        numread = benchFile.read(&rbuffer, RBUFFSIZE);
        dmicro = micros()-startmicro;
        if(dmicro > maxmicro) maxmicro = dmicro;
        if(dmicro > 999) gt500us++;
        microsum += dmicro;
    
      }
      Serial.printf("Buffer read times: average = %4.2f usec   Maximum = %lu usec    >999uSec: %lu\n",
                                  microsum/BUFFSTOREAD, maxmicro, gt500us);
      Serial.printf("Average Read Rate = %6.3f MBytes/second)", (float)(10240 *4096)/(float)microsum);
      benchFile.close();
      Serial.println();
    
    }
    
    //------------------------------------------------------------------------------
    /*
       User provided date time callback function.
       See SdFile::dateTimeCallback() for usage.
    */
    void dateTime(uint16_t* date, uint16_t* time) {
      // use the year(), month() day() etc. functions from timelib
    
      // return date using FAT_DATE macro to format fields
      *date = FAT_DATE(year(), month(), day());
    
      // return time using FAT_TIME macro to format fields
      *time = FAT_TIME(hour(), minute(), second());
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •