FatFs.f_write() stops working after writing some data

I'm on Arduino Uno (SAMD21 architecture) and I'm using FatFs to have a FAT file system. I also use Adafruit SPIFlash library to enable low-level read/write on the flash memory (so I call its APIs in the FatFs->diskio.cpp file).

The memory chip is Winbond W25Q64JVSM.

I've noticed an issue when I've started to fill a file with data from a for loop (simplified code):

    char buff[32];
    for (unsigned int i = 0; i < 399; ++i)
    {
      memset(buff, 0, sizeof(buff));           // erase the buffer
      snprintf(buff, sizeof(buff), "%d,", i);  // fill it
      f_write(file, buff, strlen(buff), &wr);  // write the string into file
    }
    f_sync(file);

When I go reading the file, I expect it to be filled with a single line containing:

0,1,2,3,4,5,.....,398,

Instead, the line always stops at a certain point, precisely "153,".
I can say that:

  • f_write never returns errors
  • If I query the size of the file, it returns the expected size of bytes
  • I did not experience this issue when writing shorter lines of just 5/10 chars.
  • For the previous point, I was thinking about a sync issue and I need to wait for an actual write operation to end, but I think I'm doing it correctly in the "diskio.cpp" file, see below.

Following relevant parts of my code:

    #define MAX_FREQ 10000000 // SAMD21 SPI chip max speed = 12MHz
    #define SECTOR_SIZE 4096
    #define PAGE_SIZE 256
    #define BLOCK_SIZE 1
    #define CHIP_SELECT 3

FatFs->diskio.cpp

    static Adafruit_FlashTransport_SPI _flashTransport(CHIP_SELECT, SPI);
    static Adafruit_SPIFlash _flash(&_flashTransport);

    /*-----------------------------------------------------------------------*/
    /* Get Drive Status                                                      */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_status (
    	BYTE pdrv		/* Physical drive nmuber to identify the drive */
    )
    {
      return RES_OK;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Inidialize a Drive                                                    */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_initialize (
    	BYTE pdrv				/* Physical drive nmuber to identify the drive */
    )
    {
      _flashTransport.setClockSpeed(MAX_FREQ, MAX_FREQ);
      if (_flash.begin()) return RES_OK;
      else return RES_ERROR;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Read Sector(s)                                                        */
    /*-----------------------------------------------------------------------*/
    
    DRESULT disk_read (
    	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
    	BYTE *buff,		/* Data buffer to store read data */
    	LBA_t sector,	/* Start sector in LBA */
    	UINT count		/* Number of sectors to read */
    )
    {
      BYTE * b = buff;
      if(_flash.readBlocks(sector, b, count)) return RES_OK;
      else return RES_ERROR;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Write Sector(s)                                                       */
    /*-----------------------------------------------------------------------*/
    
    #if FF_FS_READONLY == 0
    
    DRESULT disk_write (
    	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
    	const BYTE *buff,	/* Data to be written */
    	LBA_t sector,		/* Start sector in LBA */
    	UINT count			/* Number of sectors to write */
    )
    {
      BYTE * b = (BYTE*) buff;
      if (_flash.writeBlocks(sector, buff, count)) return RES_OK;
      else return RES_ERROR;
    }
    
    #endif
    
    
    /*-----------------------------------------------------------------------*/
    /* Miscellaneous Functions                                               */
    /*-----------------------------------------------------------------------*/
    
    DRESULT disk_ioctl (
    	BYTE pdrv,		/* Physical drive nmuber (0..) */
    	BYTE cmd,		/* Control code */
    	void *buff		/* Buffer to send/receive control data */
    )
    {
      DRESULT res = RES_ERROR;  
      
      switch( cmd )
      {
        case CTRL_SYNC :   // Make sure that data has been written  
          _flash.syncBlocks(); // <<--- this should prevent any sync issue with writing (?)
          res = RES_OK;
          break;
        
        case GET_SECTOR_COUNT : 
          *(LBA_t*)buff = _flash.size() / SECTOR_SIZE;
          res = RES_OK;
          break;  
    
        case GET_SECTOR_SIZE :
          *(WORD*)buff = SECTOR_SIZE;
          res = RES_OK;
          break;  
    
        case GET_BLOCK_SIZE :
          *(DWORD*)buff = BLOCK_SIZE;
          res = RES_OK;
          break;  
    	  
        default:  
          res = RES_PARERR;  
      }
    
      return res;
    }

The flash memory was initialized this way (already checked for bad return codes):

    BYTE work[SECTOR_SIZE];    
    MKFS_PARM p = {FM_FAT, 1, 0, 0, SECTOR_SIZE};
    f_mkfs("", &p, work, sizeof work);
    f_mount(&fs, "", 1);

Am I missing something?

(This question is also on StackOverflow: https://stackoverflow.com/questions/74265635/fatfs-f-write-stops-working-after-writing-some-data)

According the file size where your program stops (about 512 bytes) I suppose something like buffer overflow issue.

Try to move

inside the for loop.

Addition - to work properly you have to check the wr status after every write

Thanks for your reply, but I've already tried putting f_sync inside loop and results are the same.
Also regarding wr value, the code is simplified but I already check it and I confirm it returns the correct amount of written data, from 0 to 398th iteration (meaning, f_write does not stop writing at 153; it continues for the entire loop and amount of written buffer is correct.

Please show all the code regarding to the file open, writing and closing.

Here is a detailed view of the code.
Opening and writing:

FIL mapFile;
if (!openFile(&mapFile, filename, FA_WRITE | FA_CREATE_ALWAYS)) 
{
  return false;
}
else 
{
  char buff[16];
  for (unsigned int i = 0; i < POINTS_COUNT; ++i) // POINTS_COUNT  = 399
  {
    memset(buff, 0, sizeof(buff));
    if (i < POINTS_COUNT - 1) snprintf(buff, sizeof(buff), "%hu,", DATA[i]); //  unsigned short DATA[POINTS_COUNT]
    else snprintf(buff, sizeof(buff), "%hu\n", DATA[i]);
    writeToFile(&mapFile, buff, strlen(buff));
  }
  flushFile(&mapFile);
  closeFile(&mapFile);
}

Utility functions openFile, writeToFile, readFile, ...:

bool openFile(FIL* file, const char* path, uint8_t mode) 
{
  lastRes = f_open(file, path, mode);
  if (lastRes != FR_OK) {
    return false;
  }
  return true;
}

bool writeToFile(FIL* file, const void* data, const unsigned int len) 
{  
  UINT wr;
  lastRes = f_write(file, data, len, &wr);
  if (lastRes != FR_OK) {
    return false;
  }
  else if (wr != len) {
    return false;
  }
  return true;
}

int readFile(FIL* file, void* data, const unsigned int len) 
{
  UINT rd;
  lastRes = f_read(file, data, len, &rd);
  if (lastRes != FR_OK) {
    return FILE_ERROR;
  }
  else {
    if (rd < len) return FILE_END;
    else return FILE_OK;
  }
}

void flushFile(FIL* file)
{
  f_sync(file);
}

void closeFile(FIL* file) 
{
  f_close(file);
}

To read/test the written file:

FIL file;
if (!openFile(&file, mapFile, FA_READ)) return;

int res;
char buff[32];
SerialUSB.println("---");
SerialUSB.print("Size: ");
SerialUSB.println(getFileSize(file));
SerialUSB.println("---");
SerialUSB.println("Content: ");
do 
{
  memset(buff, 0, sizeof(buff));
  res = readFile(file, buff, sizeof(buff) - 1);
  
  // I also add a tag every time I execute the readFile to show the result code
  // [0] = FILE_ERROR, [1] = FILE_OK, [2] = FILE_END
  SerialUSB.print("[");
  SerialUSB.print(res);
  SerialUSB.print("]");
  if (res != FILE_ERROR) SerialUSB.print(buff);
  else 
  {
    SerialUSB.print("!!! ERROR READING ");
    SerialUSB.println(getLastFlashResult());
  }
}
while(res == FILE_OK);
SerialUSB.println();
SerialUSB.println("---");
closeFile(&file);

After writing the file with DATA filled by 0...398, the output of printFile() is:

21:26:23.530 -> ---
21:26:23.530 -> Size: 1486
21:26:23.530 -> ---
21:26:23.530 -> Content: 
21:26:23.530 -> [1]0,1,2,3,4,5,6,7,8,9,10,11,12,13[1],14,15,16,17,18,19,20,21,22,23,[1]24,25,26,27,28,29,30,31,32,33,3[1]4,35,36,37,38,39,40,41,42,43,44[1],45,46,47,48,49,50,51,52,53,54,[1]55,56,57,58,59,60,61,62,63,64,6[1]5,66,67,68,69,70,71,72,73,74,75[1],76,77,78,79,80,81,82,83,84,85,[1]86,87,88,89,90,91,92,93,94,95,9[1]6,97,98,99,100,101,102,103,104,[1]105,106,107,108,109,110,111,112[1],113,114,115,116,117,118,119,12[1]0,121,122,123,124,125,126,127,1[1]28,129,130,131,132,133,134,135,[1]136,137,138,139,140,141,142,143[1],144,145,146,147,148,149,150,15[1]1,152,153,154,15[1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][2]
21:26:23.530 -> ---

You see, the file size is correct, but there is a strange behavior after passing 512 bytes. Since "[1]" tags continues, seems like there is something else in the file, but it is not the data I expect to be.

As I mentioned, it produces the very same output also if putting f_sync inside the loop:

char buff[16];
for (unsigned int i = 0; i < POINTS_COUNT; ++i) // POINTS_COUNT  = 399
{
  memset(buff, 0, sizeof(buff));
  if (i < POINTS_COUNT - 1) snprintf(buff, sizeof(buff), "%hu,", DATA[i]); //  unsigned short DATA[POINTS_COUNT]
  else snprintf(buff, sizeof(buff), "%hu\n", DATA[i]);
  writeToFile(&mapFile, buff, strlen(buff));
  if (i % 10 == 0) flushFile(&mapFile); // <-------
}
closeFile(&mapFile);
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.