Is there a simple test for flash memory being soldered on the 4.1 correctly?

MichaelMeissner

Senior Member+
I have a Teensy 4.1 and it appears the PSRAM I soldered on to it either was mis-soldered or the chip does not work (external_psram_size is 0).

I used Paul's test for psram:

Is there a similar test for the flash memory chip? Before I get to un-soldering and re-soldering the chips, I would like to know if the flash memory at least works or if I need to replace both chips.

I recall having old programs in the past that verified flash and psram. These were written before the flash/psram was integrated into the Arduino code. So, I was wondering if there is newer code.
 
Sorry to hear you are having some issues... One thing I have done when I see things like this is to run the pin test that @defragster and myself played with...
Code:
void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 4000 );
  Serial.println("Compile Time:: " __FILE__ " " __DATE__ " " __TIME__);
  Serial.printf("Num Digital Pins: %d\n", NUM_DIGITAL_PINS);

  testForShorts();
  
}

uint32_t cnt = 0;
void loop() {
  cnt++;
    allPinTest( cnt );
}

uint32_t pinLast[NUM_DIGITAL_PINS];
void allPinTest( uint32_t cnt ) {
  uint32_t ii, SET;
  Serial.print("PULLDOWN Start Vals:\n  ");
  SET = 0;
  Serial.print("PULLDOWN :: TEST to 3.3V\n  ");
  for ( ii = 0; ii < NUM_DIGITAL_PINS; ii++) {
    pinMode( ii, INPUT_PULLDOWN );
    delayMicroseconds( 5 );
    pinLast[ii] = digitalReadFast( ii );
    if (pinLast[ii]) {
      Serial.print("\nd#=");
      Serial.print( ii );
      Serial.print( " val=" );
    }
    Serial.print( pinLast[ii] );
    Serial.print(',');
  }
  Serial.println();
  Serial.println();
  while ( 1 ) {
    uint32_t jj, dd = 0, cc = 0, ee=4;
    cc = 0;
    for ( ii = 0; ii < NUM_DIGITAL_PINS; ii++) {
      jj = digitalReadFast( ii );
      if ( jj != pinLast[ii] ) {
        dd = 1;
        cc++;
        pinLast[ii] = jj;
        Serial.print("d#=");
        Serial.print( ii );
        if ( pinLast[ii] ) Serial.print( "\t" );
        Serial.print( " val=" );
        Serial.print( pinLast[ii] );
        Serial.print(',');
      }
      if ( cc > 1 && ee ) {
        Serial.println(">>> MULTI CHANGE !!");
        ee--;
      }
      if ( Serial.available() ) {
        while ( Serial.available() ) Serial.read();
        if ( 0 == SET ) {
          SET = 1;
          Serial.print("PULLUP :: TEST TO GND\n  ");
        }
        else {
          SET = 0;
          Serial.print("PULLDOWN :: TEST to 3.3V\n  ");
        }
        for ( ii = 0; ii < NUM_DIGITAL_PINS; ii++) {
          if ( 0 == SET )
            pinMode( ii, INPUT_PULLDOWN );
          else
            pinMode( ii, INPUT_PULLUP );
          delayMicroseconds( 20 );
          pinLast[ii] = digitalReadFast( ii );
          if (SET != pinLast[ii]) {
            Serial.print("d#=");
            Serial.print( ii );
            Serial.print( " val=" );
            Serial.println( pinLast[ii] );
          }
        }
      }
    }
    if ( dd ) {
      dd = 0;
      Serial.println();
      delay( 50 );
    }
  }
}

void testForShorts() {
  uint32_t ii;
  Serial.print("Quick Test for Shorts to adjacent pin");
  Serial.println("First pull pins down and see if the next one follows");
  for ( ii = 0; ii < NUM_DIGITAL_PINS-1; ii++) {
    pinMode( ii+1, INPUT_PULLDOWN );
    pinMode( ii, OUTPUT);
    digitalWrite(ii, HIGH);
    delayMicroseconds( 5 );
    if (digitalRead(ii+1)) {
      Serial.printf("%d:%d ", ii, ii+1);
    }
  }
  Serial.println("\n Now try Pull up and see if setting low follow");
  for ( ii = 0; ii < NUM_DIGITAL_PINS-1; ii++) {
    pinMode( ii+1, INPUT_PULLUP );
    pinMode( ii, OUTPUT);
    digitalWrite(ii, LOW);
    delayMicroseconds( 5 );
    if (!digitalRead(ii+1)) {
      Serial.printf("%d:%d ", ii, ii+1);
    }
  }
  Serial.println();  
}

You can alternate between using a jumper wire from GND or 3.3v and when you touch a pin in the right mode (first test is from 3.3v to it the second test is from GND to pin) and when you touch a pin it should tell you which pin you touched.

And if there is a short between pins you may see a message like multiple pins changed state, which is a good clue.

Also at startup it tries a quick and dirty test looking for shorts, of setting all IO pins to INPUT_PULLDOWN, and one by one it sets a pin to OUTPUT and HIGH and checks for the next pin up to see if it changed state as well...

If it looks like that works, I might try something like the LFSIntegerity example sketch in the LittleFS library examples, to see if it looks like that might work. You would probably need to set the type of chip you are looking to test. Like TEST_QSPI.
 
I sometimes have the same problem with soldering.

Besides what @KurtE posted I typically run the following sketch which tests the Flash using LittleFS. If it works no problem then I move on to debugging or I will usually just touch up the pins with solder (I miss sometimes):
Code:
#include <LittleFS.h>
uint64_t fTot, totSize1;


LittleFS_QSPIFlash myfs;
//LittleFS_Program myfs;
//LittleFS_SPIFlash myfs;

#define chipSelect 3

File file, file1, file3;


#include <ctime>


void setup() {
  //pinMode(13, OUTPUT);
  pinMode(chipSelect, OUTPUT);
  digitalWrite(chipSelect, HIGH);
  while (!Serial) ; // wait

  delay(1000);
  Serial.println("LittleFS Test"); delay(5);
  if (!myfs.begin()) {
  //if (!myfs.begin(chipSelect)) {
    Serial.println("Error starting spidisk");
    while (1) ;
  }


  Serial.printf("TotalSize (Bytes): %d\n", myfs.totalSize());
  //myfs.deviceErase();

  delay(1000);
  Serial.println("started");
  //printDirectory();
  delay(10);
  Serial.println("MAKE files");
  myfs.mkdir("structureData1");
  printDirectory();
  file = myfs.open("structureData1/temp_test.txt", FILE_WRITE);
  delay(10);
  file.println("SOME DATA TO TEST");
  file.close();
  file = myfs.open("temp_test1.txt", FILE_WRITE);
  delay(10);
  file.println("SOME DATA TO TEST");
  file.close();
  file = myfs.open("temp_test2.txt", FILE_WRITE);
  delay(10);
  file.println("SOME DATA TO TEST");
  file.close();

  uint8_t buffer[] = "Test for SOME DATA TO TEST";
  Serial.println("--------------");
  file = myfs.open("temp_test3.txt", FILE_WRITE);
  uint16_t writeSize = sizeof(buffer);
  uint16_t write2Buffer = 512 / writeSize;

  uint8_t tempBuffer[512];
  memset(tempBuffer, 0xFF, 512);
  for (uint16_t j = 0; j < write2Buffer; j++) {
    for (uint16_t i = 0; i < writeSize; i++) {
      tempBuffer[j * writeSize + i] = buffer[i];
    }
  }
  for (uint16_t j = 0; j < 2; j++)
    file.write(tempBuffer, sizeof(tempBuffer));
  delay(10);

  file.close();

  delay(100);
  printDirectory();
  delay(10);

  Serial.printf("Disk Usuage:\n");
  Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize());

  uint8_t buf[2048];
  memset(buf, 0, 2048);
  file = myfs.open("temp_test3.txt", FILE_READ);
  file.read(buf, 2048);
  for (uint16_t i = 0; i < 15; i++) {
    for (uint16_t j = 0; j < sizeof(buffer); j++) {
      Serial.printf("%c", buf[j + sizeof(buffer)*i]);
    } Serial.println();
  }
  file.close();
  

}

void bigFile() {
  char someData[2048];
  memset( someData, 'z', 2048 );
  file = myfs.open("bigfile.txt  ", FILE_WRITE);
  file.write(someData, sizeof(someData));

  for (uint16_t j = 0; j < 300; j++)
    file.write(someData, sizeof(someData));
  file.close();
  printDirectory();
}

void loop() {
  if ( Serial.available() ) {
    char rr;
    rr = Serial.read();
    if (rr == 'B') bigFile2MB(1);
    if (rr == 'b') bigFile2MB(0);
    if (rr == 'q') myfs.quickFormat();
    if (rr == 'F') myfs.lowLevelFormat('.');
    if (rr == 'f') myfs.formatUnused( 0 , 0 );

    printDirectory();
  time_t t = rtc_get();
  Serial.print(std::ctime(&t));
  }
}

void printDirectory() {

  Serial.println("printDirectory\n--------------");
  printDirectory(myfs.open("/"), 0);
  Serial.println();
}


void printDirectory(File dir, int numTabs) {
  //dir.whoami();
  uint64_t fSize = 0;
  uint32_t dCnt = 0, fCnt = 0;
  if ( 0 == dir ) {
    Serial.printf( "\t>>>\t>>>>> No Dir\n" );
    return;
  }
  while (true) {
    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      Serial.printf("\n %u dirs with %u files of Size %u Bytes\n", dCnt, fCnt, fSize);
      fTot += fCnt;
      totSize1 += fSize;
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }

    if (entry.isDirectory()) {
      Serial.print("DIR\t");
      dCnt++;
    } else {
      Serial.print("FILE\t");
      fCnt++;
      fSize += entry.size();
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println(" / ");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
    //Serial.flush();
  }
}

void bigFile2MB( int doThis ) {
  char myFile[] = "/0_2MBfile.txt";
  char fileID = '0' - 1;

  if ( 0 == doThis ) {  // delete File
    Serial.printf( "\nDelete with read verify all #bigfile's\n");
    do {
      fileID++;
      myFile[1] = fileID;
      if ( myfs.exists(myFile) && bigVerify( myFile, fileID) ) {
        //filecount--;
        myfs.remove(myFile);
      }
      else break; // no more of these
    } while ( 1 );
  }
  else {  // FILL DISK
    lfs_ssize_t resW = 1;
    char someData[2048];
    uint32_t xx, toWrite;
    toWrite = 2048 * 1000;
    if ( toWrite > (65535 + (myfs.totalSize() - myfs.usedSize()) ) ) {
      Serial.print( "Disk too full! DO :: q or F");
      return;
    }
    xx = toWrite;
    Serial.printf( "\nStart Big write of %u Bytes", xx);
    uint32_t timeMe = micros();
    file3 = nullptr;
    do {
      if ( file3 ) file3.close();
      fileID++;
      myFile[1] = fileID;
      file3 = myfs.open(myFile, FILE_WRITE);
    } while ( fileID < '9' && file3.size() > 0);
    if ( fileID == '9' ) {
      Serial.print( "Disk has 9 FILES 0-8! DO :: b or q or F");
      return;
    }
    memset( someData, fileID, 2048 );
    int hh = 0;
    while ( toWrite >= 2048 && resW > 0 ) {
      resW = file3.write( someData , 2048 );
      hh++;
      if ( !(hh % 40) ) Serial.print('.');
      toWrite -= 2048;
    }
    xx -= toWrite;
    file3.close();
    timeMe = micros() - timeMe;
    file3 = myfs.open(myFile, FILE_WRITE);
    if ( file3.size() > 0 ) {
      //filecount++;
      Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() );
    }
    if ( file3 != 0 ) file3.close();
    Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) );
    Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize());
    if ( resW < 0 ) {
      Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW );
      //errsLFS++;
      myfs.remove(myFile);
    }
  }
}

bool bigVerify( char szPath[], char chNow ) {
  uint32_t timeMe = micros();
  file3 = myfs.open(szPath);
  if ( 0 == file3 ) {
    return false;
  }
  char mm;
  uint32_t ii = 0;
  uint32_t kk = file3.size() / 50;
  Serial.printf( "\tVerify %s bytes %llu : ", szPath, file3.size() );
  while ( file3.available() ) {
    file3.read( &mm , 1 );
    //rdCnt++;
    ii++;
    if ( !(ii % kk) ) Serial.print('.');
    if ( chNow != mm ) {
      Serial.printf( "<Bad Byte!  %c! = %c [0x%X] @%u\n", chNow, mm, mm, ii );
      //parseCmd( '0' );
      //errsLFS++;
      //checkInput( 1 );
      break;
    }
  }
  if (ii != file3.size()) {
    Serial.printf( "\n\tRead Count fail! :: read %u != f.size %llu\n", ii, file3.size() );
    //parseCmd( '0' );
    //errsLFS++;
    //checkInput( 1 );  // PAUSE on CmdLine
  }
  else
    Serial.printf( "\tGOOD! >>  bytes %lu", ii );
  file3.close();
  timeMe = micros() - timeMe;
  Serial.printf( "\n\tBig read&compare KBytes per second %5.2f \n", ii / (timeMe / 1000.0) );
  if ( 0 == ii ) return false;
  return true;
}
 
Hmmm, mjs513:

Code:
Teensy41_flash: In function 'void loop()':
Teensy41_flash:117: error: 'class LittleFS_QSPIFlash' has no member named 'formatUnused'
     if (rr == 'f') myfs.formatUnused( 0 , 0 );
                         ^
'class LittleFS_QSPIFlash' has no member named 'formatUnused'

If I delete the line that references it, it seems to work:

Code:
    if (rr == 'f') myfs.formatUnused( 0 , 0 );

Thanks. That means just the psram looks bad.
 
And it looks like it is pins 52 and 54 that need to be soldered on the psram. Thanks, KurtE.

Before getting to the soldering however, I was reattaching the Teensy to the RGB display I had it running, forgetting I was running pintest. There was a little smoke as I probably touched the wrong pin, but I disconnected things before things became completely damaged. FWIW, the RGB display is running fine and the flash test also ran fine after I brought things back up.

Note, in running pintest, I had a micro SD card in the micro SD card reader, and it said that pins 46 and 47 might be shorted. When I took out the SD card, it was fine.
 
Last edited:
@MM - myfs.formatUnused( 0 , 0 ); - is post bet 5 'aftermarket' code I added to pre-format unused media blocks so they can be made ready for write at full speed later without needing to FORMAT before use as those blocks come into play after they have been used and recorded as unused by LittleFS.

Hoping it seems useful for inclusion in Beta 6.

Hope the smoke you saw wasn't the magic kind ...

Did you reflow the solder on the PSRAM with some flux (paste or liquid) then clean and dry?

The PJRC PSRAM test is a nice simple self contained test - would be nice to have one for FLASH - but they don't have the setup done on startup - and there is not a single chip to accept and work with. With updates in Beta 6 of TD 1.54 it could be better with LittleFS though.
 
Yes, in terms of the smoke, evidently I didn't release enough of it, as it seems to work. :)

I used my soldering iron at the highest heat and solder braid to remove the solder from all 4 pins on the one side (3.3v, pins 54-52), leaving the other side still attached, and then 1-by-1, redid the solder. Yeah, I know I should solder chips like that by laying down solder first and then putting the chip on top of the solder and heating it, but I tend to do by having the chip directly on the pads, and then running a bit of solder on top of the pin. When I initially did the solder, I tested it my normal method of a LED/resistor and having a program that asked for a pin #, and then turned on that pin. Unfortunately, I think I tested by putting the power wire from the LED on the pad itself, and not on the metal on the chip.

It would be nice if both programs got added to the examples in 1.54 beta 6 (pintest also for that matter).
 
@MichaelMeissner - Sorry about that. As @defragster stated "myfs.formatUnused( 0 , 0 );" was an addition that was added as we were updating LittleFS. I forgot to remove it from code I posted. Right now that change as well as the addition of that example was added in the PR to LittleFS (https://github.com/PaulStoffregen/LittleFS/pull/7) when we added in the changes necessary to support FRAM, FeRAM and NAND chips.
 
After discovering this thread when wanting to verify that my flash chip is in working order after soldering it and the PSRAM to my Teensy 4.1 board, I have ran into a problem. I have poked and looked around for a solution before posting, but now I am totally stuck. I already used "teensy41_psram_memtest.ino" to verify my PSRAM and it checks out, but when running the provided code from @mjs513 to check the flash, it gives me the following error compiling:

Arduino: 1.8.13 (Windows 10), TD: 1.53, Board: "Teensy 4.1, Serial, 600 MHz, Faster, US English"
In file included from C:\Users\me\Arduino IDE\portable\sketchbook\TEST_FLASH_VIA_LittleFS\TEST_FLASH_VIA_LittleFS.ino:1:0:
C:\Users\me\Arduino IDE\portable\sketchbook\libraries\LittleFS-main\src/LittleFS.h:25:16: fatal error: FS.h: No such file or directory
compilation terminated.
Error compiling for board Teensy 4.1.

I did some poking around, and the "FS.h" file doesn't exist in the Teensy4 core:
C:\Users\me\Arduino IDE\hardware\teensy\avr\cores\teensy4

I did find it in the Teensy3 core:
C:\Users\me\Arduino IDE\hardware\teensy\avr\cores\teensy3

I copied the code provided by @mjs513 exactly, commenting out the "myfs.formatUnused( 0 , 0 );" line. I would repost it, but it seems redundant to do so. Anyone have any ideas as to what I may be missing here? Thanks!
 
It should be in both Teensy3 and Teensy4 branches. And they are identical up on github, so you should be able to simply copy the one from the Teensy3 directory to the Teensy4 directory...

Or install the latest beta releases of Teensyduino.
 
@mjs513, I apologize, it totally slipped my mind to provide the version of Teensyduino. The version I have is 1.53 (current, non-beta). Thank you both for helping @@mjs513 @KurtE. You guys are *super* fast, replies came before breakfast and shower was over lol. I think I will take both of your advice and install the beta release and see if it corrects the issue. Although I wonder why the "FS.h" file is missing from the Teensy4 core, and not the Teensy3 core? I used the installer to install Teensyduino into my Arduino IDE as usual. I will post the results shortly. Thanks again!
 
After taking the advice to upgrade to the beta7 version the code compiles as fine as powder. Big thanks @@mjs513 @KurtE for the extremely fast reply and the advice, you guys are awesome.
 
Looking back, I did provide my version of Teensyduino lol Look -->> Arduino: 1.8.13 (Windows 10), TD: 1.53, Board: "Teensy 4.1, Serial, 600 MHz, Faster, US English"
The Arduino IDE spits out Teensyduino version when outputting info from the compiler. The included " TD: 1.53 " part of that string includes Teensy version. Neat. Thanks again guys.
 
Although I wonder why the "FS.h" file is missing from the Teensy4 core, and not the Teensy3 core?

That file exists because I've been planning to add this to Teensy's core library for a very long time. But the FS.h in 1.53's teensy3 folder isn't functional. It's more of a placeholder or experiment than actual usable code. In 1.53, nothing uses or even includes it. In hindsight, I probably shouldn't have put it into the publicly published code.
 
Back
Top