• 【FatFs】手动移植FatFs,将SRAM转化为文件系统


    【FatFs】手动移植FatFs,将SRAM转化为文件系统

    1. 实验环境

    • Keil5 MDK-ARM,编译器使用ARM Compiler V6.16
    • NUCLEO-H723ZG
    • STM32CubeMX 6.5.0
    • Apr 17, 2021 FatFs R0.14b

    2. 理论部分

    因为FatFs和硬件本身没有直接关系,可以将自己想要的任何可以读写的“存储”格式化为FAT文件系统(格式化本身其实就是一个读写过程,实际上就是往“存储”中写入一定的“规范”来让程序按照预定义的方法来读写内存),所以将内置Flash、SRAM等这些存储使用FAT文件系统管理也是可行的。

    这里使用SRAM作为物理存储,需要SRAM比较大的单片机,正好手上有一块NUCLEO-H723ZG,主控为STM32H723ZGT6,有总共564 Kbytes SRAM,下图为其RAM的内存地址映射表。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZEB4v6rp-1658303686916)(https://raw.githubusercontent.com/MisakaMikoto128/TyporaPic/main/typora/Untitled.png?token=AOLAY4Q52SBW6RM4JMMSNZLC26XV6)]

    这里我希望尽量少占用些内存,避免可能的问题。因为FatFs的sector size只能取512, 1024, 2048 and 4096 bytes,所以选择最小的sector size : 512 bytes。其次为能够正确使用f_mkfs格式化,sdisk_ioctl的实现中GET_SECTOR_COUNT返回的大小至少要为191= 128 + N_SEC_TRACK(:=63),这个后面再说。这里实际上使用了512bytes * 240 的SRAM。

    3. FatFs移植步骤

    1. 得到FatFs源码,全部添加.c到工程中,并包含头文件路径
    2. 配置ffconf.h,本例中其他部分不变,只配置了FF_USE_MKFS为1,F_MAX_SS=FF_MIN_SS=512不变,FF_USE_FASTSEEK为1,FF_USE_LABEL为1,其中FF_USE_MKFS、F_MAX_SS、FF_MIN_SS是最重要的,因为要使用f_mkfs格式化存储区。
    3. 实现diskio.c中的disk_ioctl、disk_write、disk_read、disk_initialize、disk_status几个函数即可。
    4. 测试。

    4. STM32工程配置

    直接使用STM32CubeMX生成一个工程,时钟和一些其他外设配置使用STM32CubeMX的NUCLEO默认的模板,这里配置了一个USB MSC用来方便在电脑上看到模拟出来的FAT存储器。

    Untitled

    Untitled

    Untitled

    Untitled

    最够点击GENERATE CODE,打开得到的工程。添加FatFs源码到工程。工程目录如图。

    Untitled

    5. 实际实现代码

    1. 使用分散加载配置文件来管理内存,如图RAM_D1用来分配内存给一个数组,用来模拟FatFs的物理存储。这个数组为ram_disk_buf,使用二维数组单纯就是为了好理解。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4J8GKfNk-1658303686928)(https://raw.githubusercontent.com/MisakaMikoto128/TyporaPic/main/typora/Untitled%206.png?token=AOLAY4XKKTD6OA5DLMKENKTC26XVQ)]

    下面的配置都在diskio.c中操作

    /* Definitions of physical drive number for each drive */
    #define DEV_RAM		0	/* Example: Map Ramdisk to physical drive 0 */
    #define DEV_MMC		1	/* Example: Map MMC/SD card to physical drive 1 */
    #define DEV_USB		2	/* Example: Map USB MSD to physical drive 2 */
    
    #include 
    #define ram_disk_sector_num 240
    __attribute__((section(".RAM_D1"))) uint8_t ram_disk_buf[ram_disk_sector_num][FF_MAX_SS] = {0};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 因为内部SRAM没有必要初始化,状态也都是正常的,不会无法读写什么的这两个函数直接返回RES_OK。
    /*-----------------------------------------------------------------------*/
    /* 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 */
    )
    {
    	return RES_OK;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. 实现读写函数,记住的是FatFs寻址使用的是逻辑块寻址LBA (Logical Block Addressing),这里的块知道就是sector,也就是寻址的最小单位是sector,每次读需要读取一整个sector,写需要写一整个sector,擦除的大小待会再讨论。这个sector不一定必须要和实际使用的硬件如Flash相同,这取决于速度和存储效率的考量。要理解这个需要先了解一下物理存储设备。
      比如Flash因为每次写入都需要先擦除,导致了这个擦除块就是最小的写单元,这个擦除块的名字可能叫Page(如STM32F103C8T6内置Flash),可能叫Flash(如STM单片机的F4和H7系列),大小也各不相同,但是不能把硬件如Flash的sector痛FatFs的sector搞混,它们是在各自技术下的叫法罢了。
      比如说自己移植FatFs配置的sector size = 1024bytes,而实际存储的Flash最小擦除单位为一个sector = 512bytes,那么实际上FatFs再调用disk_read或者disk_write读写存储的时候应带读写两个Flash的sector。当然再读写Flash的时候也导致了一个问题,有的Falsh(如H723内置Flash Sector Size = 128KB)的擦除单位太大,导致不能直接移植。
      这里设置F_MAX_SS=FF_MIN_SS=512bytes,SRAM的话没有Flash需要擦除才能写的烦恼,直接模拟为一个sector为512bytes,存储效率最高。
    void VirualRAMDiskRead(uint8_t *buff,uint32_t sector,uint32_t count){
    		for(int i = sector; i < sector+count; i++){
    		for(int j = 0; j < FF_MAX_SS; j++)
    		{
    			buff[(i - sector)*FF_MAX_SS+j] = ram_disk_buf[i][j];
    		}
    	}
    }
    
    void VirualRAMDiskWrite(const uint8_t *buff,uint32_t sector,uint32_t count){
    	for(int i = sector; i < sector+count; i++){
    		for(int j = 0; j < FF_MAX_SS; j++)
    		{
    			ram_disk_buf[i][j] = buff[(i - sector)*FF_MAX_SS+j];
    		}
    	}
    }
    /*-----------------------------------------------------------------------*/
    /* 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 */
    )
    {
    	VirualRAMDiskRead(buff,sector,count);
    	return RES_OK;
    }
    
    /*-----------------------------------------------------------------------*/
    /* 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 */
    )
    {
    	VirualRAMDiskWrite(buff,sector,count);
    	return RES_OK;
    }
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    1. 然后是GET_SECTOR_COUNT 用于f_mkfs格式化时获取可用的sector的数量,32bit-LBA的情况下至少为191,这个参数也决定了总的FAT文件系统管理的容量,只管来说就是虚拟出来的存储容量(This command is used by f_mkfs and f_fdisk function to determine the size of volume/partition to be created. It is required when FF_USE_MKFS == 1)。GET_SECTOR_SIZE 这个再F_MAX_SS=FF_MIN_SS的情况下没有作用。GET_BLOCK_SIZE 这个是最下的擦除大小,单位是sector,如果不知道多大或者使用非Flash存储媒介就返回1。
    /*-----------------------------------------------------------------------*/
    /* 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)
      {
      /* Make sure that no pending write process */
      case CTRL_SYNC:
        res = RES_OK;
        break;
    
      /* Get number of sectors on the disk (DWORD) */
      case GET_SECTOR_COUNT :
    		*(DWORD*)buff = ram_disk_sector_num;
    		res = RES_OK;
        break;
    
      /* Get R/W sector size (WORD) */
      case GET_SECTOR_SIZE :
    		*(DWORD*)buff = FF_MAX_SS;
    		res = RES_OK;
        break;
    
        /* Get erase block size in unit of sector (DWORD) */
      case GET_BLOCK_SIZE :
    		*(DWORD*)buff = 1;
    		res = RES_OK;
        break;
    
      default:
        res = RES_PARERR;
      }
    
      return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    1. 实现过去时间的函数,不想真的实现就返回0,但是必须要有这个函数的实现,否则会报错。
    /**
      * @brief  Gets Time from RTC
      * @param  None
      * @retval Time in DWORD
      */
    DWORD get_fattime(void)
    {
      /* USER CODE BEGIN get_fattime */
      return 0;
      /* USER CODE END get_fattime */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 为了方便查看实现了一下USB MSC的接口。这里不详细讲解,其实和FatFs在移植上很像。这些操作都在usbd_storage_if.c中实现。
    /** @defgroup USBD_STORAGE_Private_Defines
      * @brief Private defines.
      * @{
      */
    
    #define STORAGE_LUN_NBR                  1    //lun数量
    /*
    STORAGE_BLK_NBR                  
    使用FatFs的f_mkfs格式化时这个参数没有什么作用,因为已经格式化好了。
    但是不能小于160 = 128+32,小于的话电脑直接无法载入存储。
    在没有使用FatFs的f_mkfs格式化时,这个参数决定了电脑上你格式化时显示的容量。
    */
    #define STORAGE_BLK_NBR                  240 //对应FatFs的sector counter
    /*
    STORAGE_BLK_SIZ                  
    这个参数不能乱写,主要时底层实现的时候是按照512来计算的,写错的话电脑直接无法载入存储
    */
    #define STORAGE_BLK_SIZ                  512 //对应FatFs的sector size
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    参数设置有问题的情况

    参数设置有问题的情况

    然后实现一下STORAGE_Read_HS和STORAGE_Write_HS即可

    /**
      * @brief  Reads data from the medium.
      * @param  lun: Logical unit number.
      * @param  buf: data buffer.
      * @param  blk_addr: Logical block address.
      * @param  blk_len: Blocks number.
      * @retval USBD_OK if all operations are OK else USBD_FAIL
      */
    int8_t STORAGE_Read_HS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
    {
      /* USER CODE BEGIN 13 */
      UNUSED(lun);
    	VirualRAMDiskRead(buf,blk_addr,blk_len);
      return (USBD_OK);
      /* USER CODE END 13 */
    }
    
    /**
      * @brief  Writes data into the medium.
      * @param  lun: Logical unit number.
      * @param  buf: data buffer.
      * @param  blk_addr: Logical block address.
      * @param  blk_len: Blocks number.
      * @retval USBD_OK if all operations are OK else USBD_FAIL
      */
    int8_t STORAGE_Write_HS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
    {
      /* USER CODE BEGIN 14 */
      UNUSED(lun);
    	VirualRAMDiskWrite(buf,blk_addr,blk_len);
      return (USBD_OK);
      /* USER CODE END 14 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    6. 测试代码

    main.c中实现

    /* USER CODE BEGIN 2 */
    	fatfs_register();
    	fatfs_rw_test("0:2021.txt");
    	fatfs_rw_test("0:2021.txt");
    	fatfs_rw_test("0:2022.txt");
    	fatfs_rw_test("0:2023.txt");
    	fatfs_rw_test("0:2024.txt");
    	fatfs_rw_test("0:2025.txt");
    	fatfs_rw_test("0:2026.txt");
    	fatfs_rw_test("0:2027.txt");
    	//fatfs_unregister();
    
      /* USER CODE END 2 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    FRESULT  f_res;
    FATFS fs;
    FIL file;
    BYTE work[FF_MAX_SS]; /* Work area (larger is better for processing time) */
    void fatfs_register()
    {
        f_res = f_mount(&fs, "0:", 1);
        if(f_res == FR_NO_FILESYSTEM)
        {
            printf("no file systems\r\n");
            f_res = f_mkfs("0:", 0, work, sizeof work);
            if(f_res != FR_OK)
                printf("mkfs failed err code = %d\r\n",f_res);
            else
                printf("init file systems ok\r\n");
        }
        else if(f_res != FR_OK)
        {
            printf("f_mount failed err code = %d\r\n",f_res);
        }
        else
        {
            printf("have file systems\r\n");
        }
    }
    void fatfs_unregister()
    {
    		f_res = f_mount(0, "0:", 0); /* Unmount the default drive */
        if(f_res != FR_OK)
        {
            printf("file systems unregister failed err code = %d\r\n",f_res);
        }
        else
        {
            printf("file systems unregister ok\r\n");
        }
    }
    
    void fatfs_rw_test(const char * path)
    {
        uint8_t w_buf[] = "this is a test txt\n now time is 2020-9-17 22:13\nend";
        uint8_t r_buf[200] = {0};
        UINT w_buf_len = 0;
        UINT r_buf_len = 0;
        f_res = f_open(&file, path, FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
        if(f_res != FR_OK)
        {
    			printf("open %s failed err code = %d\r\n",path,f_res);
        }
        else
        {
    	     printf("open %s  ok\r\n",path);
        }
        f_res = f_write(&file, w_buf, sizeof(w_buf), &w_buf_len);
        if(f_res != FR_OK)
        {
    	     printf("write %s failed  err code = %d\r\n",path,f_res);
        }
        else
        {
    	      printf("write %s  ok   w_buf_len = %d\r\n", path,w_buf_len);
            f_lseek(&file, 0);
            f_res = f_read(&file, r_buf, f_size(&file), &r_buf_len);
            if(f_res != FR_OK)
            {
    	         printf("read %s failed f_res = %d\r\n", path,f_res);
            }
            else
            {
    	         printf("read %s  ok   r_buf_len = %d\r\n", path,r_buf_len);
            }
        }
    		printf("file %s at sector = %d,cluster = %d\r\n",path,file.sect,file.clust);
    		f_close(&file);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    7. FatFs中sector数量的最小限制

    查看ff.c中f_mkfs的实现可以看到关于卷大小的限制的代码,b_vol: basic vloume size, sz_vol: size of volume。

    Untitled

    Untitled

    8. 一些不理解的问题

    实验中总的分配了120KB的内存。下面是一些不太理解的问题,打算仔细读一下FAT文件系统的specs在来看看。

    实验中发现修改GET_BLOCK_SIZE,使用f_mkfs格式化得到的模拟U盘在电脑上显示的大小不一样,越小得到的越大。但是总的有120K,实际最大显示88.5KB。

    直接使用MSC(Mass Storage Class)来映射到电脑上管理显示的大小是正确的,格式化以后有100KB。

    GET_BLOCK_SIZE 得到 256时f_mkfs格式化失败。
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    Speedoffice(excel)文档中min函数如何使用
    java毕业设计汽车4S服务管理系统mybatis+源码+调试部署+系统+数据库+lw
    JavaSE 第十二章 IO流
    软件测试之TCP、HTTP协议必知必会,面试必备
    初识网络原理
    PY32F003F18的GPIO函数
    几种在ARM MCU上控制流水灯的方法
    网络基础aaa
    最全的100个Python精选库,建议收藏!
    Linux基础篇-逻辑卷管理
  • 原文地址:https://blog.csdn.net/qq_42820594/article/details/125892537