FatFs R0.14 - FF_USE_LFN

试验原因

产品板子上想改下程序,改之前好好的.
将lwip换到最新版后,在FatFs操作时崩溃. 是调用 f_stat()引起的,在FatFs里面崩了。莫名奇妙…
想单独作个试验,移植最新版的FatFs R0.14来验证一下。
FatFs旧版也正常,就是我改完程序引起的问题,估计是内存申请失败引起的问题。

在开发板上单独验证了SPI方式SD卡驱动库配合FatFs的f_x函数的使用。看看调用f_stat()是否会崩溃。
f_stat没崩,那我将FatFs R0.14移植到产品板子,如果还有问题,再调试.

不过发现要注意的几个小知识点.

文件读的模式不能为FA_OPEN_APPEND

  • 读文件时的参数3,不能是或上FA_OPEN_APPEND,否则读到的文件字节数都是0,即使文件有内容。因为FA_OPEN_APPEND方式打开文件后,seek到文件尾部去了。再读就读不到了.
char sz_read_buf[0x100] = {'\0'};
void test_fatfs_read_file()
{
  FRESULT file_rc = FR_OK;
  FIL fp;
  FSIZE_t file_size = 0;
  FSIZE_t file_size_left = 0;
  UINT br = 0;
//  const char* psz_msg = "hello fatfs";

  // mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
  // f_open(
  file_rc = f_stat(psz_file_on_dir, &fno);
  printf("%s : %s = f_stat(%s)\n", ((FR_OK == file_rc) ? "ok" : "err"), FRESULT2psz(file_rc), psz_file_on_dir);
  if (FR_OK == file_rc) {
    // file not exist, create it
    file_rc = f_open(&fp, psz_file_on_dir, FA_READ); // 参数3不能或上 FA_OPEN_APPEND, 否则seek到尾部去了, 读到的内容长度是0
    printf("%s : %s = f_open(%s)\n", ((FR_OK == file_rc) ? "ok" : "err"), FRESULT2psz(file_rc), psz_file_on_dir);
    if (FR_OK == file_rc) {
      file_size = f_size(&fp);
      printf("[%s] file size = [%d]\n", psz_file_on_dir, file_size);
      
      file_size_left = file_size;
      do {
        printf("file_size_left = %d\n", file_size_left);
        if (file_size_left <= 0) {
          break;
        }

        memset(sz_read_buf, 0, sizeof(sz_read_buf));
        file_rc = f_read(&fp, sz_read_buf, sizeof(sz_read_buf) - 1, &br);
        printf("%s : %s = f_read(&fp), bw = %d\n", ((FR_OK == file_rc) ? "ok" : "err"), FRESULT2psz(file_rc), br);
        
        if ((FR_OK != file_rc) || (0 == br)) {
          break;
        }
        
        printf("read : %s\n", sz_read_buf);
        file_size_left -= br;
      } while (1);
      
      file_size = f_size(&fp);
      printf("[%s] file size = [%d]\n", psz_file_on_dir, file_size);

      file_rc = f_close(&fp);
      printf("%s : %s = f_close(&fp)\n", ((FR_OK == file_rc) ? "ok" : "err"), FRESULT2psz(file_rc));
    }
  }
}

FF_USE_LFN = 1引起的崩溃问题

FF_USE_LFN 是在 \fatfs\ffconf.h 定义的配置值. 管长文件名的操作。

#define FF_USE_LFN		3
#define FF_MAX_LFN		255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/   0: Disable LFN. FF_MAX_LFN has no effect.
/   1: Enable LFN with static  working buffer on the BSS. Always NOT thread-safe.
/   2: Enable LFN with dynamic working buffer on the STACK.
/   3: Enable LFN with dynamic working buffer on the HEAP.
/
/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/  be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
/  specification.
/  When use stack for the working buffer, take care on stack overflow. When use heap
/  memory for the working buffer, memory management functions, ff_memalloc() and
/  ff_memfree() exemplified in ffsystem.c, need to be added to the project. */

FF_USE_LFN默认的配置是0, 这在实际应用中肯定不行. http_server素材啥的, 都是同事在windows下调试好的, 不可能是8.3文件名格式。FF_USE_LFN按照说明, 可以取0~3. 因为我测试,是串行的代码。线尝试将FF_USE_LFN改成1, 但是发现写文件后,关闭文件句柄的时候,挂掉了。看起来是栈溢出。
挂掉的调用链
打开文件(f_open)
写一些内容(f_write() 很短(e.g. hello fatfs), 不超过30字节)
// 以下调用进了FatFs
f_close() 这是主动调用, 下面的函数调用都在FatFs中被动调用。
f_sync()
sync_fs()
mem_set(fs->win, 0, sizeof fs->win); // 在这个函数中执行都正常,但是无法返回到调用点,看起来栈溢出了.

/* Fill memory block */
static void mem_set (void* dst, int val, UINT cnt)
{
	BYTE *d = (BYTE*)dst;

	do {
		*d++ = (BYTE)val;
	} while (--cnt);
}

mem_set()看起来很干净,甚至连大块栈上空间都没用. 不可能是这个函数有问题。
mem_set()只在挂掉的这条调用链上才挂掉。下过断点,其他调用该链过了这里几次,都是正常的。
说明我测试这条调用链比较长,用的栈空间比较多吧。
下位机的栈是这样,没多大一点地方。
FF_USE_LFN改成2是不行的,这也是在栈上调用。没有去试,在栈上凯辟空间不靠谱。
尝试将FF_USE_LFN改成3,这是在堆上分配空间。

FF_USE_LFN = 3不能用标准库的内存管理函数。

将FF_USE_LFN改成3后,需要自己实现ff_memalloc()/ff_memfree().
FatFs官方代码中直接调用的malloc()/free()

#if FF_USE_LFN == 3	/* Dynamic memory allocation */

/*------------------------------------------------------------------------*/
/* Allocate a memory block                                                */
/*------------------------------------------------------------------------*/

void* ff_memalloc (	/* Returns pointer to the allocated memory block (null if not enough core) */
	UINT msize		/* Number of bytes to allocate */
)
{
	return malloc(msize);	/* Allocate a new memory block with POSIX API */
}


/*------------------------------------------------------------------------*/
/* Free a memory block                                                    */
/*------------------------------------------------------------------------*/

void ff_memfree (
	void* mblock	/* Pointer to the memory block to free (nothing to do if null) */
)
{
	free(mblock);	/* Free the memory block with POSIX API */
}

#endif

又跑了几遍程序。发现测试程序出现了我打印的错误ITM信息。
是内存分配失败。同样的内存块(512bytes), 申请/释放成对调用,只需要8次,就会分配失败…

// 使用malloc()/free()的运行结果记录, 从下面的运行结果来看, 要自己实现内存池才靠谱.
// 下面的运行结果,可以看出都是一块内存成对的malloc()/free(), 应该是内存碎片引起的分配失败

//  >> main
//  >> test_fatfs
//  ok : f_mount(sd:), rc = 0
//  ok : 0x20002348 = fatfs_malloc(512), gi_cnt_for_fatfs_malloc = 1
//  ok : fatfs_free(0x20002348), gi_cnt_for_fatfs_free = 1
//  err : FR_NO_FILE = f_stat(sd:test1_file_not_exist.txt)
//  ok : 0x20002348 = fatfs_malloc(512), gi_cnt_for_fatfs_malloc = 2
//  ok : fatfs_free(0x20002348), gi_cnt_for_fatfs_free = 2
//  err : FR_EXIST = f_mkdir(sd:log)
//  ok : 0x20002348 = fatfs_malloc(512), gi_cnt_for_fatfs_malloc = 3
//  ok : fatfs_free(0x20002348), gi_cnt_for_fatfs_free = 3
//  ok : FR_OK = f_stat(sd:log)
//  ok : 0x20002348 = fatfs_malloc(512), gi_cnt_for_fatfs_malloc = 4
//  ok : fatfs_free(0x20002348), gi_cnt_for_fatfs_free = 4
//  err : FR_NO_FILE = f_stat(sd:log/my_file_not_exist.txt)
//  ok : 0x20002348 = fatfs_malloc(512), gi_cnt_for_fatfs_malloc = 5
//  ok : fatfs_free(0x20002348), gi_cnt_for_fatfs_free = 5
//  ok : FR_OK = f_stat(sd:log/my_log.txt)
//  ok : 0x20002348 = fatfs_malloc(512), gi_cnt_for_fatfs_malloc = 6
//  ok : fatfs_free(0x20002348), gi_cnt_for_fatfs_free = 6
//  ok : FR_OK = f_unlink(sd:log/my_log.txt)
//  ok : 0x20002348 = fatfs_malloc(512), gi_cnt_for_fatfs_malloc = 7
//  ok : fatfs_free(0x20002348), gi_cnt_for_fatfs_free = 7
//  err : FR_NO_FILE = f_stat(sd:log/my_log.txt)
//  ok : 0x20002348 = fatfs_malloc(512), gi_cnt_for_fatfs_malloc = 8
//  ok : fatfs_free(0x20002348), gi_cnt_for_fatfs_free = 8
//  ok : FR_OK = f_open(sd:log/my_log.txt)
//  ok : FR_OK = f_close(&fp)
//  err : malloc(512), gi_cnt_for_fatfs_malloc = 9
//  err : FR_NOT_ENOUGH_CORE = f_stat(sd:log/my_log.txt)

以前看ucos的文档,人家说在下位机中调用库函数中的malloc()/free()很危险,果真不虚。
将以前自己封装的星翼的内存管理库整进来,在ff_memalloc()/ff_memfree()调用自己的内存管理函数。再跑几次,都正常。问题解决。
\fatfs\ffsystem.c

/*------------------------------------------------------------------------*/
/* Allocate a memory block                                                */
/*------------------------------------------------------------------------*/

void* ff_memalloc (	/* Returns pointer to the allocated memory block (null if not enough core) */
	UINT msize		/* Number of bytes to allocate */
)
{
  // return (void*)malloc(msize);	/* Allocate a new memory block with POSIX API */
	return fatfs_malloc(msize);	/* Allocate a new memory block with POSIX API */
}

/*------------------------------------------------------------------------*/
/* Free a memory block                                                    */
/*------------------------------------------------------------------------*/

void ff_memfree (
	void* mblock	/* Pointer to the memory block to free (nothing to do if null) */
)
{
  // free(mblock);	/* Free the memory block with POSIX API */
	fatfs_free(mblock);	/* Free the memory block with POSIX API */
}

\fatfs\diskio.c

static int gi_cnt_for_fatfs_malloc = 0;
static int gi_cnt_for_fatfs_free = 0;

void* fatfs_malloc( UINT size )
{
  // @todo
  char* psz_malloc = ram_malloc_in(size);
  
  gi_cnt_for_fatfs_malloc++;
  
  if (NULL == psz_malloc) {
    printf("err : malloc(%d), gi_cnt_for_fatfs_malloc = %d\n", size, gi_cnt_for_fatfs_malloc);
  } else {
    printf("ok : 0x%p = fatfs_malloc(%d), gi_cnt_for_fatfs_malloc = %d\n", psz_malloc, size, gi_cnt_for_fatfs_malloc);
  }
  
  return psz_malloc;
}

void fatfs_free( void* ptr )
{
  // @todo
  if (NULL != ptr) {
    gi_cnt_for_fatfs_free++;
    ram_free_in(ptr);
    printf("ok : fatfs_free(0x%p), gi_cnt_for_fatfs_free = %d\n", ptr, gi_cnt_for_fatfs_free);
    ptr = NULL;
  }
}

试验工程

ST_STM32F407_StdPeriph_Templates_FatFs_2020_0319_1726.zip

环境 : STM32F407 + SPI方式SD卡驱动库 + SPI2上的SD卡外接插座 + FatFs R0.14 + 对星翼内存管理库的二次封装库
功能 : 对SPI方式的SD卡内的16GB TF卡(Fat32文件格式),进行读写测试。将工程中能用到的FatFs的f_x函数都测试了一下,f_mount()挂载磁盘/卸载磁盘, f_stat()查看文件是否存在, f_mkdir()建立文件夹, f_open()打开文件, f_close()关闭文件, f_unlink()删除文件, f_write()写文件, f_read()读文件. 文件操作都正常。

硬件试验快照

SD卡外接模块到开发板自己焊的,焊的怪丑的:P
连接线用的网线, 皮实.
看来SPI连线长一些,也不影响SPI时序。应该等长线(差不多)就行。

你可能感兴趣的:(#,STM32)