What is the maximum extra psram on a teensy 4.1?

Status
Not open for further replies.
Hello. Just wondering what is the maximum extra psram than can be easily added to a Teensy 4.1?

Obviously I have read this page, and I understand you can add either 2x 8MB ram, or 8MB ram and 8MB flash:

but I notice that the audio board also has a spot for a memory chip, so if an audio board is attached to the 4.1, can we add another 8MB of ram or flash there too? or is that just there to allow memory expansion for 4.0 users only?

Thanks!
 
Note, in the flash memory slot on the Teensy 4.1, you can put larger flash memory chips. The largest that I'm aware of is a 256MB/2G-bit NAND Flash chip (Winbond W25N02KVZEIR). You would use the LittleFS_QPINAND constructor with Littlefs to access it as a file system.

Whether you can access memory in a flash chip as normal memory, I don't know. I've only used flash memory as a replacement for SD cards. If you need to access it as normal volatile memory, I suspect using 2 8MB/64M-bit PSram chips may be your only option.

The flash memory on the audio board can only be accessed like a file system (i.e. the memory is not available to the Teensy as normal memory, you have to do explicit calls to write a series of bytes to the flash or read from them. I recall there were only a limited number of chips that worked with the library. Of course if you are going to do explicit calls to read/write the memory, you might as well use the micro SD card in the Teensy 4.1 and audio board to get access to gigabytes of memory.

Note, in terms of accessing things via file system reads/writes, you also have the 'extra' space in the Teensy flash memory that you can use. IIRC, the Teensy 4.0 has roughly 1 megabyte available and the Teensy 4.1 has roughly 7 megabytes available.

In terms of how to access the various memories as a file system, I've used this MTP example that I've modified to make all of the entries available:

Code:
// From /shared/arduino/git/MTP_t4.git/trunk/examples/mtp-basic/mtp-basic.ino

#include <SPI.h>
#include <SD.h>
#include <MTP.h>

#if defined(ARDUINO_TEENSY41)
// The Teensy 4.1 has two sets of solder pads underneath the Teensy that you
// can solder either 2 PSram chips, 1 flash memory chip, or 1 flash memory chip
// and a PSram chip.  The smaller flash chips tend to use NOR flash while the
// larger flash chips tend to use NAND flash.
#define USE_LFS_RAM            1    // T4.1 PSRAM (or RAM)
#define USE_LFS_QSPI_NOR        1    // T4.1 QSPI NOR flash (16MB)
#define USE_LFS_QSPI_NAND        1    // T4.1 QSPI NAND flash (128MB)

#else
#define USE_LFS_RAM            0    // T4.1 PSRAM (or RAM)
#define USE_LFS_QSPI_NOR        0    // T4.1 QSPI NOR flash (16MB)
#define USE_LFS_QSPI_NAND        0    // T4.1 QSPI NAND flash (128MB)
#endif

#if defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
// Teensy 4.0 and 4.1 can use the upper flash memory as a file system via
// LittleFS.  The top bytes (64K for Teensy 4.0 and 256K for Teensy 4.1) of
// this memory is reserved for EEPROM emulation and the LED blink restore
// program.  On the Teensy 4.0 you have 1 megabyte - 64K of flash for a
// filesystem.  On the Teensy 4.1, you have 7 megabytes - 256K of flash for the
// filesystem.
#define USE_LFS_PROGM            1    // T4.0/T4.1 Progam Flash
#else
#define USE_LFS_PROGM            0    // T4.0/T4.1 Progam Flash
#endif

#define USE_SD                1    // Use either the built-in micro SD card reader or the reader on pin 10 (audio adapter)
#define USE_LFS_SPI            1    // SPI Flash (soldered to the audio adapter or the built-in flash on the prop shield)

// If you use the audio adapter revision A through C on a Teensy LC, 3.2, 3.5,
// or 3.6, it needs to remap the MOSI/SCK pins because those pins (11 and 13)
// are also I2S pins used by the audio shield.  But the prop shield wants to
// use the normal definitions.  In general, I've used the prop shield mostly on
// the Teensy 3.2 and LC, while if I use it, I use the audio shield on the
// Teensy 3.5 or Teensy 3.6.
//
// The Teensy 4.0 and 4.1 uses the audio adapter revision D, and it uses the
// standard SPI pins.
#if defined(ARDUINO_TEENSY35) || defined(ARDUINO_TEENSY36)
#define USE_TEENSY3_AUDIO_SHIELD    1    // Whether to use the Audio shield MOSI/SCLK for Teensy 3.x processors
#else
#define USE_TEENSY3_AUDIO_SHIELD    0    // Use standard configuration
#endif

#if USE_EVENTS==1
  extern "C" int usb_init_events(void);
#else
  int usb_init_events(void) {}
#endif

#if USE_LFS_RAM==1 ||  USE_LFS_PROGM==1 || USE_LFS_QSPI_NOR==1 || USE_LFS_QSPI_NAND==1 || USE_LFS_SPI==1
  #include "LittleFS.h"
#endif

#if defined(__IMXRT1062__)
  // following only as long usb_mtp is not included in cores
  #if !__has_include("usb_mtp.h")
    #include "usb1_mtp.h"
  #endif
#else
  #ifndef BUILTIN_SDCARD 
    #define BUILTIN_SDCARD 254
  #endif
  void usb_mtp_configure(void) {}
#endif


/****  Start device specific change area  ****/
// SDClasses 
#if USE_SD==1
  // edit SPI to reflect your configuration
#if USE_TEENSY3_AUDIO_SHIELD
 // Teensy audio adapter for Teensy 3.2, 3.5, and 3.6 (revision A-C) needs to remap the MOSI and SCK pins
  #define SD_MOSI  7
  #define SD_MISO 12
  #define SD_SCK  14
#else
 // Standard Teensy configuration
  #define SD_MOSI 11
  #define SD_MISO 12
  #define SD_SCK  13
#endif

  #define SPI_SPEED SD_SCK_MHZ(33)                // adjust to sd card 

  #if defined (BUILTIN_SDCARD)
    const char *sd_str[]={"BUILTIN-SD", "AUDIO-SD"};        // edit to reflect your configuration
    const int cs[] = {BUILTIN_SDCARD, 10};            // edit to reflect your configuration
  #else
    const char *sd_str[]={"AUDIO-SD"};                // edit to reflect your configuration
    const int cs[] = {10};                    // edit to reflect your configuration
  #endif
  const int nsd = sizeof(sd_str)/sizeof(const char *);

SDClass sdx[nsd];
#endif

//LittleFS classes
#if USE_LFS_RAM==1
  const char *lfs_ram_str[]    = {"RAM1","RAM2"};        // edit to reflect your configuration
  const int lfs_ram_size[]    = {2'000'000, 4'000'000};    // edit to reflect your configuration
  const int nfs_ram        = sizeof(lfs_ram_str)/sizeof(const char *);

  LittleFS_RAM ramfs[nfs_ram]; 
#endif

#if USE_LFS_QSPI_NOR==1
  const char *lfs_qspi_nor_str[]    = {"QSPI-NOR"};        // edit to reflect your configuration
  const int nfs_qspi_nor        = sizeof(lfs_qspi_nor_str)/sizeof(const char *);

  LittleFS_QSPIFlash qspifs_nor[nfs_qspi_nor]; 
#endif

#if USE_LFS_QSPI_NAND==1
  const char *lfs_qspi_nand_str[]    = {"QSPI-NAND"};    // edit to reflect your configuration
  const int nfs_qspi_nand        = sizeof(lfs_qspi_nand_str)/sizeof(const char *);

  LittleFS_QPINAND qspifs_nand[nfs_qspi_nand];
#endif

#if USE_LFS_PROGM==1
  const char *lfs_progm_str[]={"PROGRAM"};            // edit to reflect your configuration
  const int lfs_progm_size[] = {1'000'000};            // edit to reflect your configuration
  const int nfs_progm = sizeof(lfs_progm_str)/sizeof(const char *);

  LittleFS_Program progmfs[nfs_progm]; 
#endif

#if USE_LFS_SPI==1
  const char *lfs_spi_str[]={"SPI-FLASH"};            // edit to reflect your configuration
  const int lfs_cs[] = {6};                    // edit to reflect your configuration
  const int nfs_spi = sizeof(lfs_spi_str)/sizeof(const char *);

LittleFS_SPIFlash spifs[nfs_spi];
#endif


MTPStorage_SD storage;
MTPD    mtpd(&storage);

void storage_configure()
{
  #if USE_SD==1
    #if defined SD_SCK
      SPI.setMOSI(SD_MOSI);
      SPI.setMISO(SD_MISO);
      SPI.setSCK(SD_SCK);
    #endif

    for(int ii=0; ii<nsd; ii++)
    { 
      #if defined(BUILTIN_SDCARD)
        if(cs[ii] == BUILTIN_SDCARD)
        {
          if(!sdx[ii].sdfs.begin(SdioConfig(FIFO_SDIO))) 
          { Serial.printf("SDIO Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
          }
          else
          {
            storage.addFilesystem(sdx[ii], sd_str[ii]);
            uint64_t totalSize = sdx[ii].totalSize();
            uint64_t usedSize  = sdx[ii].usedSize();
            Serial.printf("SDIO Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
            Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
          }
        }
        else if(cs[ii]<BUILTIN_SDCARD)
      #endif
      {
        pinMode(cs[ii],OUTPUT); digitalWriteFast(cs[ii],HIGH);
        if(!sdx[ii].sdfs.begin(SdSpiConfig(cs[ii], SHARED_SPI, SPI_SPEED))) 
        { Serial.printf("SD Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
        }
        else
        {
          storage.addFilesystem(sdx[ii], sd_str[ii]);
          uint64_t totalSize = sdx[ii].totalSize();
          uint64_t usedSize  = sdx[ii].usedSize();
          Serial.printf("SD Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
          Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
        }
      }
    }
    #endif

    #if USE_LFS_RAM==1
    for(int ii=0; ii<nfs_ram;ii++)
    {
      if(!ramfs[ii].begin(lfs_ram_size[ii])) 
      { Serial.printf("Ram Storage %d %s failed or missing",ii,lfs_ram_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(ramfs[ii], lfs_ram_str[ii]);
        uint64_t totalSize = ramfs[ii].totalSize();
        uint64_t usedSize  = ramfs[ii].usedSize();
        Serial.printf("RAM Storage %d %s ",ii,lfs_ram_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif

    #if USE_LFS_PROGM==1
    for(int ii=0; ii<nfs_progm;ii++)
    {
      if(!progmfs[ii].begin(lfs_progm_size[ii])) 
      { Serial.printf("Program Storage %d %s failed or missing",ii,lfs_progm_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(progmfs[ii], lfs_progm_str[ii]);
        uint64_t totalSize = progmfs[ii].totalSize();
        uint64_t usedSize  = progmfs[ii].usedSize();
        Serial.printf("Program Storage %d %s ",ii,lfs_progm_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif

    #if USE_LFS_QSPI_NOR==1
    for(int ii=0; ii<nfs_qspi_nor;ii++)
    {
      if(!qspifs_nor[ii].begin()) 
      { Serial.printf("QSPI Storage %d %s failed or missing",ii,lfs_qspi_nor_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(qspifs_nor[ii], lfs_qspi_nor_str[ii]);
        uint64_t totalSize = qspifs_nor[ii].totalSize();
        uint64_t usedSize  = qspifs_nor[ii].usedSize();
        Serial.printf("QSPI NOR Storage %d %s ",ii,lfs_qspi_nor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif

    #if USE_LFS_QSPI_NAND==1
    for(int ii=0; ii<nfs_qspi_nand;ii++)
    {
      if(!qspifs_nand[ii].begin()) 
      { Serial.printf("QSPI Storage %d %s failed or missing",ii,lfs_qspi_nand_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(qspifs_nand[ii], lfs_qspi_nand_str[ii]);
        uint64_t totalSize = qspifs_nand[ii].totalSize();
        uint64_t usedSize  = qspifs_nand[ii].usedSize();
        Serial.printf("QSPI NAND Storage %d %s ",ii,lfs_qspi_nand_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif

    #if USE_LFS_SPI==1
    for(int ii=0; ii<nfs_spi;ii++)
    {
      if(!spifs[ii].begin(lfs_cs[ii])) 
      { Serial.printf("SPIFlash Storage %d %d %s failed or missing",ii,lfs_cs[ii],lfs_spi_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(spifs[ii], lfs_spi_str[ii]);
        uint64_t totalSize = spifs[ii].totalSize();
        uint64_t usedSize  = spifs[ii].usedSize();
        Serial.printf("SPIFlash Storage %d %d %s ",ii,lfs_cs[ii],lfs_spi_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif
}

void setup()
{ 
  while (!Serial && millis () < 3000)
    ;

  Serial.println("MTP_test");
  Serial.printf ("USE_LFS_RAM              = %d\n", USE_LFS_RAM);
  Serial.printf ("USE_LFS_QSPI_NOR         = %d\n", USE_LFS_QSPI_NOR);
  Serial.printf ("USE_LFS_QSPI_NAND        = %d\n", USE_LFS_QSPI_NAND);
  Serial.printf ("USE_LFS_PROGM            = %d\n", USE_LFS_PROGM);
  Serial.printf ("USE_SD                   = %d\n", USE_SD);
  Serial.printf ("USE_LFS_SPI              = %d\n", USE_LFS_SPI);
  Serial.printf ("USE_TEENSY3_AUDIO_SHIELD = %d\n", USE_TEENSY3_AUDIO_SHIELD);
  Serial.println ("");

  #if USE_EVENTS==1
    usb_init_events();
  #endif

  #if !__has_include("usb_mtp.h")
    usb_mtp_configure();
  #endif
  storage_configure();

}

void loop()
{ 
  mtpd.loop();

#if USE_EVENTS==1
  if(Serial.available())
  {
    char ch=Serial.read();
    Serial.println(ch);
    if(ch=='r') 
    {
      Serial.println("Reset");
      mtpd.send_DeviceResetEvent();
    }
  }
#endif
}
 
Last edited:
You may need to define "easily". And indeed "to a Teensy 4.1", since you mention adding PSRAM to the audio adaptor... And maybe what your goal is would give us a clue, though to be fair that doesn't necessarily make much difference.

For example, for use in audio delay effects, we have this post and following. That adds 8 off 8MB PSRAMs, albeit on a separate PCB of which @dougcl may or may not have spares, or otherwise grant access. Assuming that, you then have to solder it up. Along with 2x PSRAMs on the Teensy 4.1 itself, that gives you 80MB total, but fragmented into 64MB with slow SPI access plus the faster on-board 16MB. I know the board works because he was kind enough to send me a sample, which I assembled and used to update the memory options for AudioEffectDelayExternal.

You can indeed fit a single PSRAM to the audio adaptor - it's not supported by any Teensyduino version to date, though, apart from the "obvious" route of "it's a PSRAM on the SPI bus".
 
Thanks for the replies, appreciated. A lot to think about.

Yeah, regarding my goal, I just have a project I'm planning, that would require quite a few audio processes running at once, including some that require memory, so just wrapping my head around the requirements. Thanks.
 
Safest to make your plans around 16.5 MByte, assuming roughly half of the on-chip 1MB RAM will be needed for a variety of other things.

Memory over slow SPI usually isn't very useful because of the overhead. Especially if you're planning to run several audio processing tasks, you probably want to have most of Teensy's 600 MHz CPU performance available for that work. Avoid use of slow SPI.
 
The Teensy microcontroller supports a maximum of 8MB of external PSRAM (Pseudo Static RAM). This additional PSRAM expands the memory capacity of the Teensy, enabling it to handle more complex and data-intensive tasks. With increased memory resources, developers can implement advanced applications and processes, making the Teensy an even more versatile platform for embedded systems and projects.maximum of 8MB of external PSRAM (Pseudo Static RAM). This additional PSRAM expands the memory capacity of the Teensy, enabling it to handle more complex and data-intensive tasks. With increased memory resources, developers can implement advanced applications and processes, making the Teensy an even more versatile platfmaximum of 8MB of external PSRAM (Pseudo Static RAM). This additional PSRAM expands the memory capacity of the Teensy, enabling it to handle more complex and data-intmaximum of 8MB of external PSRA

chatgpt is a fool and a convincing liar
 
OP's question has been answered, can we lock the thread before any more AI bots decided to jump in?
 
Thanks for the replies everyone.

Safest to make your plans around 16.5 MByte, assuming roughly half of the on-chip 1MB RAM will be needed for a variety of other things.

Memory over slow SPI usually isn't very useful because of the overhead. Especially if you're planning to run several audio processing tasks, you probably want to have most of Teensy's 600 MHz CPU performance available for that work. Avoid use of slow SPI.

Thanks, what do you mean by slow SPI in this context?
Apologies if I'm misunderstanding, do you mean using regular SPI pins to connect a memory module is slower than soldering a QSPI chip to the PSRAM solder pads on the back of the board? Thanks.
 
Thanks for the replies everyone.



Thanks, what do you mean by slow SPI in this context?
Apologies if I'm misunderstanding, do you mean using regular SPI pins to connect a memory module is slower than soldering a QSPI chip to the PSRAM solder pads on the back of the board? Thanks.
Standard SPI is one bit per cycle of the clock - that runs more slowly.
QSPI is QUAD or four bits per transfer and runs at a faster clock rate.
And in the case of QSPI PSRAM it is memory mapped and as such the chip transfer interface is fully automated by the processor.
 
do you mean using regular SPI pins to connect a memory module is slower than soldering a QSPI chip to the PSRAM solder pads on the back of the board?

Yes, exactly, a massive difference in raw speed.

In addition to all the points Defragster made, when memory mapped you also gain the (usually) huge benefit of M7's cache.
 
Status
Not open for further replies.
Back
Top