3

I am implementing a file system on SPI flash memory using a w25qxx chip and an STM32F4xx on STM32CubeIDE. I have successfully created the basic i/o for the w25 over SPI, being able to write and read sectors at a time.

In my user_diskio.c I have implemented all of the needed i/o methods and have verified that they are properly linked and being called.

in my main.cpp I go to format the drive using f_mkfs(), then get the free space, and finally open and close a file. However, f_mkfs() keeps returning FR_MKFS_ABORTED. (FF_MAX_SS is set to 16384)

  fresult = FR_NO_FILESYSTEM;
  if (fresult == FR_NO_FILESYSTEM)
  {
      BYTE work[FF_MAX_SS]; // Formats the drive if it has yet to be formatted
      fresult = f_mkfs("0:", FM_ANY, 0, work, sizeof work);
  }

  f_getfree("", &fre_clust, &pfs);

  total = (uint32_t)((pfs->n_fatent - 2) * pfs->csize * 0.5);
  free_space = (uint32_t)(fre_clust * pfs->csize * 0.5);

  fresult = f_open(&fil, "file67.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
  f_puts("This data is from the FILE1.txt. And it was written using ...f_puts... ", &fil);
  fresult = f_close(&fil);

  fresult = f_open(&fil, "file67.txt", FA_READ);
  f_gets(buffer, f_size(&fil), &fil);
  f_close(&fil);

Upon investigating my ff.c, it seems that the code is halting on line 5617:

if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */

n_clst is calculated a few lines up before some conditional logic, on line 5594:

n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau;

Here is what the debugger reads the variables going in as:

enter image description here

This results in n_clst being set to 4294935040, as it is unsigned, though the actual result of doing the calculations would be -32256 if the variable was signed. As you can imagine, this does not seem to be an accurate calculation.

The device I am using has 16M-bit (2MB) of storage organized in 512 sectors of 4kb in size. The minimum erasable block size is 32kb. If you would need more info on the flash chip I am using, page 5 of this pdf outlines all of the specs.

This is what my USER_ioctl() looks like:

DRESULT USER_ioctl (
    BYTE pdrv,      /* Physical drive nmuber (0..) */
    BYTE cmd,       /* Control code */
    void *buff      /* Buffer to send/receive control data */
)
{
  /* USER CODE BEGIN IOCTL */
    UINT* result = (UINT*)buff;
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11, GPIO_PIN_SET);

    switch (cmd) {
        case GET_SECTOR_COUNT:
            result[0] = 512; // Sector and block sizes of
            return RES_OK;
        case GET_SECTOR_SIZE:
            result[0] = 4096;
            return RES_OK;
        case GET_BLOCK_SIZE:
            result[0] = 32768;
            return RES_OK;

    }
    return RES_ERROR;
  /* USER CODE END IOCTL */
}

I have tried monkeying around with the parameters to f_mkfs(), swapping FM_ANY out for FM_FAT, FM_FAT32, and FM_EXFAT (along with enabling exFat in my ffconf.h. I have also tried using several values for au rather than the default. For a deeper documentation on the f_mkfs() method I am using, check here, there are a few variations of this method floating around out there.

1
  • I've used FatFS to access a SPI flash chip I made available over USB. Clifford is right, if you only want internal access, other filesystems are better. Commented Feb 25, 2021 at 9:34

2 Answers 2

4

Here:

fresult = f_mkfs("0:", FM_ANY, 0, work, sizeof work);

The second argument is not valid. It should be a pointer to a MKFS_PARM structure or NULL for default options, as described at http://elm-chan.org/fsw/ff/doc/mkfs.html.

You should have something like:

MKFS_PARM fmt_opt = {FM_ANY, 0, 0, 0, 0};
fresult = f_mkfs("0:", &fmt_opt, 0, work, sizeof work);

except that it is unlikely for your media (SPI flash) that the default option are appropriate - the filesystem cannot obtain formatting parameters from the media as it would for SD card for example. You have to provide the necessary formatting information.

Given your erase block size I would guess:

 MKFS_PARM fmt_opt = {FM_ANY, 0, 32768, 0, 0};

but to be clear I have never used the ELM FatFS (which STM32Cube incorporates) with SPI flash - there may be additional issues. I also do not use STM32CubeMX - it is possible I suppose that the version has a different interface, but I would recommend using the latest code from ELM rather than ST's possibly fossilised version.

Another consideration is that FatFs is not particularly suitable for your media due to wear-levelling issues. Also ELM FatFs has not journalling or check/repair function, so is not power fail safe. That is particularly important for non-removable media that you cannot easily back-up or repair.

You might consider a file system specifically designed for SPI NOR flash such as SPIFFS, or the power-fail safe LittleFS. Here is an example of LittleFS in STM32: https://uimeter.com/2018-04-12-Try-LittleFS-on-STM32-and-SPI-Flash/

2
  • Thank you so much for your help! I have decided to switch to littleFS. As for the bad parameter in the method, I think you are referring to a different distribution of FatFS. The one I am using came with STM32CubeIDE, and is documented in the link. Commented Feb 25, 2021 at 15:49
  • 2
    @JonathanJust - same library different version; I am using an even older version that lacks the opt parameter altogether. Good decision on LittleFS. I would generally avoid most all of the STM32Cube nonsense. It is a collection of poorly documented parts from various sources and varying quality - the worst being the parts contributed by ST themselves. Their HAL is a dog made worse by the documentation. You can write your own bugs, you don't need the chip vendor to do it for you ;-)
    – Clifford
    Commented Feb 25, 2021 at 18:10
0

Ok, I think the real problem was that the IOCTL call GET_BLOCK_SIZE to get the block size was returning the sector size instead of the number of sectors in the block. Which is usually 1 for SPI Flash.

Not the answer you're looking for? Browse other questions tagged or ask your own question.