Raspberry Pi Pico TinyUSB SDFAT USB MSC Reading after Write

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
disasterarea
 
Posts: 7
Joined: Fri May 16, 2014 2:22 pm

Raspberry Pi Pico TinyUSB SDFAT USB MSC Reading after Write

Post by disasterarea »

I'm having an issue with something in the TinyUSB or SDFat libraries, whereby I can write files to the flash of a Pi Pico successfully, but when I try to read them back they are incorrect.

Here's the code:

Code: Select all

// MIT License header skipped for forum post
#include "SPI.h"
#include "SdFat.h"
#include "Adafruit_SPIFlash.h"
#include "Adafruit_TinyUSB.h"


Adafruit_FlashTransport_RP2040 flashTransport;

Adafruit_SPIFlash flash(&flashTransport);

// file system object from SdFat
FatVolume fatfs;

FatFile root;
FatFile file;

// USB Mass Storage object
Adafruit_USBD_MSC usb_msc;

// Check if flash is formatted
bool fs_formatted;

// Set to true when PC write to flash
bool fs_changed;

// the setup function runs once when you press reset or power the board
void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);

  flash.begin();

  // Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
  usb_msc.setID("Adafruit", "External Flash", "1.0");

  // Set callback
  usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);

  // Set disk size, block size should be 512 regardless of spi flash page size
  usb_msc.setCapacity(flash.size() / 512, 512);

  // MSC is ready for read/write
  usb_msc.setUnitReady(true);

  usb_msc.begin();

  // Init file system on the flash
  fs_formatted = fatfs.begin(&flash);

  Serial.begin(115200);
  while ( !Serial ) delay(10);   // wait for native usb

  if ( !fs_formatted )
  {
    Serial.println("Failed to init files system, flash may not be formatted");
  }

  Serial.println("Adafruit TinyUSB Mass Storage External Flash example");
  Serial.print("JEDEC ID: 0x"); Serial.println(flash.getJEDECID(), HEX);
  Serial.print("Flash size: "); Serial.print(flash.size() / 1024); Serial.println(" KB");

  fs_changed = true; // to print contents initially
}

void loop()
{
  if ( fs_changed )
  {
    fs_changed = false;

    //    delay(1000);

    // check if host formatted disk
    if (!fs_formatted)
    {
      fs_formatted = fatfs.begin(&flash);
    }

    // skip if still not formatted
    if (!fs_formatted) return;

    File32 readFile = fatfs.open("test.hex", FILE_READ);

    if (!readFile) {
      Serial.println("Error, failed to open test.hex for reading!");
      //      while (1) yield();
    }
    else {
      Serial.print("Entire contents of test.hex, ");
      Serial.print(readFile.available(), DEC);
      Serial.println(" bytes");
      while (readFile.available()) {
        //      delay(1);
        char c = readFile.read();
        if (c < 255) {
          Serial.print(c);
        }
        else {
          Serial.println(" ERROR"); // don't print non-ASCII chars
          break;
        }
      }
    }

    readFile.close();

  }


  delay (5000);  // don't refresh all the time

}



// callbacks copied verbatim from msc_external_flash example

// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
  // Note: SPIFLash Block API: readBlocks/writeBlocks/syncBlocks
  // already include 4K sector caching internally. We don't need to cache it, yahhhh!!
  return flash.readBlocks(lba, (uint8_t*) buffer, bufsize / 512) ? bufsize : -1;
}

// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
{
  digitalWrite(LED_BUILTIN, HIGH);

  // Note: SPIFLash Block API: readBlocks/writeBlocks/syncBlocks
  // already include 4K sector caching internally. We don't need to cache it, yahhhh!!
  return flash.writeBlocks(lba, buffer, bufsize / 512) ? bufsize : -1;
}

// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_cb (void)
{
  // sync with flash
  flash.syncBlocks();

  // clear file system's cache to force refresh
  fatfs.cacheClear();

  fs_changed = true;

  digitalWrite(LED_BUILTIN, LOW);
}
If I start the program with "test.hex" already on the drive, the results are:

Code: Select all

Entire contents of test.hex, 1448 bytes
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
:4001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BF
:40014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007F
:40018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003F
:4001C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF
:020200000000FC
:4008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B8
:020840000000B6
:321000004A29A514000000000000000000000000000000000000000000000000FF7F000000000000A08CE001E0016009C0030000C40630
:0A180000FFFF10A4200010D4B6036F
:00000001FF
This is correct - it's an Intel .HEX file, all ASCII characters and 1488 bytes in length including the 0x0A at the end of each line.

If I instead copy the file to the drive while the program is running, I get this:

Code: Select all

Entire contents of test.hex, 1448 bytes
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
:4000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ERROR
This is wrong - the file reads 1024 bytes and then reads in a value for "c" that is greater than 255, throwing the ERROR message and aborting reads. If I don't have the break statement in the error checking, the serial monitor fills up with ¿ characters until Arduino runs out of memory.

Frustratingly, sometimes this all actually works, and I get all 1488 bytes read. Sometimes I only get 512 bytes read. But the file always copies correctly, if I read it back it's identical to the source. And if I restart the Pico with the file already in flash, the results are correct. Both good and bad results are showing me 1488 bytes in the file, but the bad reads only get part of the result before it chokes and gives me an error.

What am I doing wrong here? The file is copying correctly, all the MSC stuff seems to be working fine. I think the issue might be with the SDFatLib, but this is my first time using it and so I'm not sure where to start looking to debug this.

Thanks!

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”