FATFS移植 - 基于N32G4FR在SD卡(sd nand)上移植FATFS文件系统

2023-07-19 17:19:48 雷龙发展

文章目录

FATFS移植 - 基于N32G4FR在SD卡(sd nand)上移植FATFS文件系统

1. 前言

2. FATFS文件系统介绍

2.1 文件系统的概念和作用

2.2 FATFS的特点和优势

3. 相关源码获取

3.1 FatFs文件源码下载

3.2 国民技术N32软件开发套件获取

4. 文件架构说明

4.1 FatFs文件架构说明

4.1.1 本地离线文档访问方式

4.1.2 源码文件介绍

4.2 国民技术N32文件架构说明

5. FatFs移植

5.1 SD卡/SD nand读写实现

5.2 FatFs移植

5.2.1 添加源文件

5.2.2 添加设备号

5.2.3 修改 `disk_status()` 函数

5.2.4 修改 `disk_initialize()` 函数

5.2.5 修改 `disk_read()` 函数

5.2.6 修改 `disk_write()` 函数

5.2.7 修改 `disk_ioctl()` 函数

5.2.8 修改 `ffconf.h` 取消 `get_fattime` 实现

5.2.9 接口测试

5.2.10 调用通用文件操作接口进行读写测试

5.2.10.1 `f_mount()` 函数

5.2.10.2 `f_mkfs()` 函数

5.2.10.3 读写测试

6. 结束语

————————————————

1. 前言

FATFS(File Allocation Table File System)是一个轻量级的文件系统,被广泛应用于嵌入式系统和小型存储设备中。它由Chan提供,并在嵌入式系统中得到了广泛的应用和支持。


FatFS官网地址为:FatFs - Generic FAT Filesystem Module。


本文主要分享关于 FatFS 文件系统在SD卡/SD nand上的移植使用,本文所采用的硬件环境如下:


  • 控制器:国民技术 N32G4FR,Cortex-M4内核控制器

  • SD nand:创世 CSNP4GCR01-AMW,4Gb SD nand


SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

2. FATFS文件系统介绍

2.1 文件系统的概念和作用

在计算机系统中,文件系统是用于组织和管理存储介质上的文件和目录的一种结构。文件系统提供了对文件的读取、写入、删除、重命名等操作,以及对目录的创建、删除、遍历等操作。它使得用户和应用程序能够方便地访问和管理存储设备上的数据。


2.2 FATFS的特点和优势

FATFS是一个开源的文件系统,具有以下特点和优势:


  1. 轻量级和高效性:FATFS是一个轻量级的文件系统,适用于资源受限的嵌入式系统和小型存储设备。它的代码量相对较小,占用的存储空间较少,并且具有较高的运行效率。


  2. 跨平台兼容性:FATFS可以在多个操作系统和平台上运行,包括嵌入式系统、Windows、Linux等。这使得开发人员可以方便地将FATFS应用于不同的硬件平台和操作系统环境中。


  3. 易于集成和使用:FATFS的源代码结构清晰,具有简单的API接口,易于集成到嵌入式系统的应用程序中。开发人员可以通过简单的函数调用来实现文件的读写、目录的创建和遍历等操作。


  4. 支持多种存储介质:FATFS支持多种存储介质,包括SD卡、SPI Flash、硬盘等。它可以根据不同的存储介质进行配置和适配,并提供统一的文件系统接口。


  5. 支持多种文件操作:FATFS支持常见的文件操作,如文件的打开、关闭、读取、写入、定位等。它还支持目录的创建、删除、重命名和遍历,以及文件和目录的属性管理。


  6. 支持长文件名和短文件名:FATFS同时支持长文件名(Long File Name,LFN)和短文件名(Short File Name,SFN)。这使得文件系统更加灵活和兼容,能够处理各种不同的文件命名规则。


  7. 支持文件系统的格式化和检查:FATFS提供了格式化和检查文件系统的功能。开发人员可以通过相应的API函数来格式化存储介质并创建文件系统,也可以进行文件系统的检查和修复。这使得文件系统能够保持良好的状态,提高数据的可靠性和完整性。


  8. 支持文件的读写缓存:FATFS允许开发人员配置读写缓存,以提高文件的读写性能。通过使用读写缓存,可以减少对存储介质的频繁访问,提高文件的读写速度。


  9. 具有错误处理和容错机制:FATFS提供了错误处理和容错机制,能够检测和处理各种错误情况。例如,当存储介质出现错误或不可用时,FATFS能够进行相应的错误处理和恢复,保证文件系统的稳定性和可靠性。


3. 相关源码获取

3.1 FatFs文件源码下载

移植FatFs,第一步当然就是获取对应的移植文件了, FatFs的源文件在其官网上即可直接下载,地址:FatFs - Generic FAT Filesystem Module

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音



3.2 国民技术N32软件开发套件获取

国民技术作为一款国产的IC,对标stm32,性能上也还是很不错了,不过其开发资料下载会有点麻烦,不过有幸你看到这里,就不会感觉很麻烦了,其开发资料下载方法如下:


1.打开电脑的文件管理器,输入以下内容:ftp://download.nationstech.com ,之后回车访问

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

2.此远程文件夹就是国民技术所有IC的全部资料了,之后便可根据自己手上所使用的IC,下载对应的资料,此处我们使用的是N32G4FR系列芯片,进入1-Microcontrollers 目录,选择对应的压缩包单击鼠标右键,选择 复制到文件夹 进行下载

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

4. 文件架构说明

4.1 FatFs文件架构说明

从官网上下载FatFs文件解压后,主要有两个目录:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  • documents:主要存放关于FatFs有关离线本地文档,文档内容和官网一致,等同于一个本地的副本,可以很好解决官网访问速度过慢的问题


  • source:里面存放了FatFs有关的源码


4.1.1 本地离线文档访问方式

使用本地浏览器打开 documents 目录下的 00index_e.html 文件,即可访问一个和官网完全一样的网页文档!且文档内的超链接也可直接点击跳转!


此文档非常重要,在FatFs文件使用中,很多API的接口,我们都需要参考此文档才行!

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

4.1.2 源码文件介绍

进入source 目录我们即可看到 fatfs 所有源码文件,7个文件,非常的精简,其各自功能如下:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

综上,其实完成FatFs文件的移植,主要是修改 diskio.c 和 ffconf.h 这两个文件就可以了,如此简单!


在 diskio.c 文件内实现物理存储器的读写实体访问,在ffconf.h 内实现FatFs文件系统子功能的开关,之后就可以使用FatFs文件系统了!


4.2 国民技术N32文件架构说明

N32 资料压缩包下载后,内容如下,里面包含了对应系列芯片的所有资料,手册、应用笔记、软硬件资料等等。

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

其中我们关注最核心的几个文件,路径如下:


  • 芯片包路径如下:N32G4FR_V3.0.0\6-软件开发套件(Software Development Kit)


  • 例程Demo路径如下:N32G4FR_V3.0.0\6-软件开发套件(Software Development Kit)\Nationstech.N32G4FR_Library.2.1.0\projects\n32g4fr_EVAL\examples


5. FatFs移植

5.1 SD卡/SD nand读写实现

SD卡和SD nand只是封装上存在差异,软件上没有区别,因此后续不作区分


我们需要在SD nand上移植FatFs文件系统,肯定首先需要实现对SD nand的基本读写访问,之后再在读写访问的基础上给它穿上文件系统这层外套了,这就像一件一件穿衣服一样。


关于SD nand的读写操作,我们可以直接打开N32对应配套的SDIO例程,例程已完成了对SD卡的读写测试,我们直接拿过来测一下,可以跑起来说明硬件上就没有什么问题,关于例程的优化可以后续再慢慢进行。


例程路径:N32G4FR_V3.0.0\6-软件开发套件(Software Development Kit)\Nationstech.N32G4FR_Library.2.1.0\projects\n32g4fr_EVAL\examples\SDIO

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

对应例程硬件配置如下:


  1.     1、SystemClock:144MHz

  2.     2、DMA通道:DMA2_CH4

  3.     3、SDIO 配置:

  4.             D0   -->   PC8          50MHz,AF_PP

  5.             D1   -->   PC9          50MHz,AF_PP

  6.             D2   -->   PC10         50MHz,AF_PP

  7.             D3   -->   PC11         50MHz,AF_PP

  8.             CLK   -->  PC12         50MHz,AF_PP

  9.             CMD   -->  PD2          50MHz,AF_PP


  10.             分频系数:178    (SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + 分频系数))

  11.             上升沿有效

  12.             禁用旁路

  13.             禁用时钟保持

  14.             总线位宽4bit

  15.     

  16.     4、USART1配置:

  17.             TX  -->  PA9            50MHz,AF_PP

  18.             波特率:115200

  19.             数据位:8bit

  20.             停止位:1bit

  21.             无校验


  22.     5、测试步骤与现象

  23.         a,测试前请先安装好TF卡

  24.         b,编译下载代码复位运行

  25.         c,从串口看打印信息,验证结果

例程正确运行后,串口打印结果如下:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音




SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

备注:

如果例程运行不起来,硬件也确认没有问题,尝试修改下SD卡识别过程中的速度,具体内容如下:


sdio_sdcard.c文件 710 行,SDIO_InitStructure.ClkDiv = 178; 修改为 SDIO_InitStructure.ClkDiv = 179;

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

为什么修改这里呢?

因为我觉得这里Demo里面可能有错误,此IC主频为144MHz,看时钟树,对应此处HCLK应该也是144MHz,那么ClkDiv的值应该配置为179时,SDIO_CK才为400KHz,Demo里面的配置应该是800KHz!

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音


实测SDIO CLK亦是如此,确实是800k,而对于部分SD卡,识别过程最大仅支持400K,因此建议可以尝试修改此分频系数值,排除此原因!

通过main.c文件的demo可知,关于SDIO访问SD nand的核心是如下几个函数:

  • 初始化SD nand

  1. Status = SD_Init(0, 3, 4);

  2.     if (Status != SD_OK)

  3.     {

  4.         printf("SD Card initialization failed!\r\n");

  5.         return testResult;

  6.     }


  • 写SD nand,同时注意写区分多块写和单块写操作

  1. #ifdef MUL_BLOCK_RW

  2.     Status = SD_WriteMultiBlocks(Buf_TX, 0x01, BLOCK_SIZE, 4);

  3. #else

  4.     Status = SD_WriteBlock(Buf_TX, 0x00, BLOCK_SIZE);

  5. #endif

  6.     

  7.     Status = SD_WaitWriteOperation();

  8.     while (SD_GetStatus() != SD_TRANSFER_OK);

  9.     if (Status != SD_OK)

  10.     {

  11.         printf("SD Card write block failed!\r\n");

  12.         return testResult;

  13.     }


  • 读SD nand,注意读也区分多块读和单块读

  1. #ifdef MUL_BLOCK_RW

  2.     Status = SD_ReadMultiBlocks(Buf_RX, 0x00, BLOCK_SIZE, 4);

  3. #else

  4.     Status = SD_ReadBlock(Buf_RX, 0x00, BLOCK_SIZE);

  5. #endif

  6.     

  7.     Status = SD_WaitReadOperation();

  8.     while (SD_GetStatus() != SD_TRANSFER_OK);

  9.     if (Status != SD_OK)

  10.     {

  11.         printf("SD Card read block failed!\r\n");

  12.         return testResult;

  13.     }

5.2 FatFs移植

5.2.1 添加源文件

  1. 1.拷贝FatFs至工程路径

  2. SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  3. 2.将相关源文件添加进工程

  4. SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  5. 3.添加相关头文件路径

  6. SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

4.打开diskio.c文件,采用SD nand读写及初始化接口完成对应 diskio.c 文件内函数的实现,对应FatFs文档内的 Media Access Interface,实现下述接口即可使用FatFs。

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音


5.2.2 添加设备号

首先,包含对应sd nand相关程序的头文件,并定义对应的物理设备号

  1. #include "ff.h" /* Obtains integer types */

  2. #include "diskio.h" /* Declarations of disk functions */

  3. #include "./sdio_sdcard/sdio_sdcard.h"


  4. /* Definitions of physical drive number for each drive */

  5. // #define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */

  6. // #define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */

  7. // #define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */

  8. #define DEV_SD    0


FatFs通过物理设备号区别实际的设备,如果我们的系统上有两个硬件存储设备,且均需移植FatFs,则应再次定义,且设备号应不一样。


同时 ffconf.h 文件内的 FF_VOLUMES 配置项,用于设置FatFs实际支持的物理设备数量上限,如果有超过一个以上的物理设备使用FatFs,需要修改此参数

  1. #define FF_VOLUMES 1

  2. /* Number of volumes (logical drives) to be used. (1-10) */

5.2.3 修改 disk_status() 函数

在线文档链接 disk_status

disk_status() 用于获取存储设备的状态,函数原型如下:

  1. DSTATUS disk_status (

  2.   BYTE pdrv     /* [IN] Physical drive number */

  3. );

传入参数:


  • pdrv:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。

返回值:


  • DSTATUS:返回驱动器的状态,FatFs仅关心 STA_NOINIT 和 STA_PROTECT

  • STA_NOINIT: 表示驱动器尚未初始化

  • STA_NODISK:表示驱动器中没有介质

  • STA_PROTECT:表示介质被写保护,如果 STA_NODISK 状态位为1,则此位无效

注意,每个状态占据一个bit位,返回值为三个数据位的或。

  1. /* Disk Status Bits (DSTATUS) */


  2. #define STA_NOINIT 0x01 /* Drive not initialized */

  3. #define STA_NODISK 0x02 /* No medium in the drive */

  4. #define STA_PROTECT 0x04 /* Write protected */

根据SD nand操作接口,修改实现如下:

  1. DSTATUS disk_status (

  2.     BYTE pdrv /* Physical drive nmuber to identify the drive */

  3. )

  4. {

  5.     int ret  = 0, result = 0;

  6.   

  7.     if (pdrv == DEV_SD) {

  8.         ret = SD_GetStatus();

  9.         if(ret == SD_TRANSFER_ERROR)

  10.             result |= STA_NODISK;

  11.         else

  12.             result = 0;

  13.     }


  14.     return result;

  15. }

5.2.4 修改 disk_initialize() 函数

在线文档链接 disk_initialize

disk_initialize() 此函数初始化存储设备并使其准备好进行通用读/写,当函数成功时,返回值中的STA_NOINIT标志被清除,函数原型如下:

  1. DSTATUS disk_initialize (

  2.   BYTE pdrv            /* [IN] Physical drive number */

  3. );

传入参数:

  • pdrv:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。

返回值:

  • DSTATUS:该函数返回当前驱动器状态标志作为结果。参考disk_status() 函数

根据SD nand操作接口,修改实现如下:

  1. /*-----------------------------------------------------------------------*/

  2. /* Inidialize a Drive                                                    */

  3. /*-----------------------------------------------------------------------*/


  4. DSTATUS disk_initialize (

  5.     BYTE pdrv /* Physical drive nmuber to identify the drive */

  6. )

  7. {

  8.     int ret;


  9.     ret = SD_Init(0, 3, 4);

  10.     if (ret != SD_OK)

  11.         return STA_NOINIT;

  12.     else

  13.         return 0;

  14. }

5.2.5 修改 disk_read() 函数

在线文档链接 disk_read

disk_read() 用于从存储设备中读取数据,函数原型如下:

  1. DRESULT disk_read (

  2.   BYTE pdrv,     /* [IN] Physical drive number */

  3.   BYTE* buff,    /* [OUT] Pointer to the read data buffer */

  4.   LBA_t sector,  /* [IN] Start sector number */

  5.   UINT count     /* [IN] Number of sectros to read */

  6. );


传入参数:

  • pdrv:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。

  • buff:指向存储读取数据的字节数组的指针。

  • sector:起始扇区号,注意此处数据类型LBA_t是DWORD或QWORD的别名,具体取决于配置选项。

  • count:读取的扇区数。


返回值:

  • DRESULT

  • RES_OK : 成功

  • RES_ERROR:错误

  • RES_PARERR:无效参数

  • RES_NOTRDY:设备尚未初始化

  1. /* Results of Disk Functions */

  2. typedef enum {

  3. RES_OK = 0, /* 0: Successful */

  4. RES_ERROR, /* 1: R/W Error */

  5. RES_WRPRT, /* 2: Write Protected */

  6. RES_NOTRDY, /* 3: Not Ready */

  7. RES_PARERR /* 4: Invalid Parameter */

  8. } DRESULT;


根据SD nand操作接口,修改实现如下:

  1. /*-----------------------------------------------------------------------*/

  2. /* Read Sector(s)                                                        */

  3. /*-----------------------------------------------------------------------*/


  4. DRESULT disk_read (

  5.     BYTE pdrv, /* Physical drive nmuber to identify the drive */

  6.     BYTE *buff, /* Data buffer to store read data */

  7.     LBA_t sector, /* Start sector in LBA */

  8.     UINT count /* Number of sectors to read */

  9. )

  10. {

  11.     int ret;


  12.     if (count == 1) {

  13.         ret = SD_ReadBlock(buff, sector * 512, 512);

  14.     } else {

  15.         ret = SD_ReadMultiBlocks(buff, sector * 512, 512, count);

  16.     }

  17.     if (ret != SD_OK)   goto error;

  18.     ret = SD_WaitReadOperation();

  19.     if (ret != SD_OK)   goto error;

  20.     while (SD_GetStatus() != SD_TRANSFER_OK);

  21.     if (ret != SD_OK)   goto error;


  22.     return RES_OK;

  23. error:

  24.     return RES_ERROR;

  25. }


关于 disk_read 有以下注意事项:


  1. 对通用存储设备(如存储卡、硬盘和光盘)的读/写操作是以称为扇区的数据字节块单位进行的,FatFs 支持 512 到 4096 字节范围内的扇区大小。当 FatFs 配置为固定扇区大小(FF_MIN_SS == FF_MAX_SS,这是大多数情况)时,通用读/写功能必须仅在此扇区大小下工作。当 FatFs 配置为可变扇区大小(FF_MIN_SS < FF_MAX_SS )时,在disk_initialize函数成功后使用disk_ioctl函数查询介质的扇区大小。


  2. 此外,关于 buff 传递的内存地址,它不一定是内存对齐的,这个需要大家在使用的时候注意。


5.2.6 修改 disk_write() 函数

在线文档链接 disk_write

disk_write() 用于往存储设备中写入数据,函数原型如下:

  1. DRESULT disk_write (

  2.   BYTE pdrv,        /* [IN] Physical drive number */

  3.   const BYTE* buff, /* [IN] Pointer to the data to be written */

  4.   LBA_t sector,     /* [IN] Sector number to write from */

  5.   UINT count        /* [IN] Number of sectors to write */

  6. );


传入参数:

  • pdrv:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。

  • buff:指向存储被写入数据的字节数组的指针。

  • sector:起始扇区号,注意此处数据类型LBA_tDWORDQWORD的别名,具体取决于配置选项。

  • count:读取的扇区数。


返回值:

  • DRESULT

    • RES_OK : 成功

    • RES_ERROR:错误

    • RES_WRPRT:设备处于写保护状态

    • RES_PARERR:无效参数

    • RES_NOTRDY:设备尚未初始化

  1. /* Results of Disk Functions */

  2. typedef enum {

  3. RES_OK = 0, /* 0: Successful */

  4. RES_ERROR, /* 1: R/W Error */

  5. RES_WRPRT, /* 2: Write Protected */

  6. RES_NOTRDY, /* 3: Not Ready */

  7. RES_PARERR /* 4: Invalid Parameter */

  8. } DRESULT;


根据SD nand操作接口,修改实现如下:

  1. /*-----------------------------------------------------------------------*/

  2. /* Write Sector(s)                                                       */

  3. /*-----------------------------------------------------------------------*/


  4. #if FF_FS_READONLY == 0


  5. DRESULT disk_write (

  6.     BYTE pdrv, /* Physical drive nmuber to identify the drive */

  7.     const BYTE *buff, /* Data to be written */

  8.     LBA_t sector, /* Start sector in LBA */

  9.     UINT count /* Number of sectors to write */

  10. )

  11. {

  12.     int ret;


  13.     if (count == 1) {

  14.         ret = SD_WriteBlock((uint8_t *)buff, sector * 512, 512);

  15.     } else {

  16.         ret = SD_WriteMultiBlocks((uint8_t *)buff, sector * 512, 512, count);

  17.     }

  18.     if (ret != SD_OK)   goto error;

  19.     ret = SD_WaitWriteOperation();

  20.     if (ret != SD_OK)   goto error;

  21.     while (SD_GetStatus() != SD_TRANSFER_OK);

  22.     if (ret != SD_OK)   goto error;


  23.     return RES_OK;

  24. error:

  25.     return RES_ERROR;

  26. }


  27. #endif


关于 disk_write() 有以下注意事项:


  1. buff 参数和disk_read一样,不一定是字节对齐的,需要注意

  2. 多个扇区的写请求通常不建议拆解成单个的扇区写操作,这回降低传输速率,但是我们在使用的时候也需要注意对应的SD卡/SD nand所支持的最大连续写入扇区数限制!

  3. 调用disk_write()函数不要求写操作完成后才返回,可以在写过程或数据丢入缓存后返回,但是需要注意返回后,buff指针所指向的内容将变成非法的,不能再使用。写完成操作完成,可在调用 disk_ioctl() 函数CTRL_SYNC 命令时完成。通过延迟写入的实现,可增大文件系统的吞吐量(加快读写速度的点子!!!)


5.2.7 修改 disk_ioctl() 函数

在线文档链接 disk_ioctl

disk_ioctl() 用于控制设备的除了读写之外的其他特定功能,函数原型如下:

  1. DRESULT disk_ioctl (

  2.   BYTE pdrv,     /* [IN] Drive number */

  3.   BYTE cmd,      /* [IN] Control command code */

  4.   void* buff     /* [I/O] Parameter and data buffer */

  5. );


传入参数:

  • pdrv:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。

  • cmd:命令代码。

  • buff:指针参数,具体取决于 cmd 参数。



返回值:

  • DRESULT

  • RES_OK : 成功

  • RES_ERROR:错误

  • RES_PARERR:无效参数

  • RES_NOTRDY:设备尚未初始化


  1. /* Results of Disk Functions */

  2. typedef enum {

  3. RES_OK = 0, /* 0: Successful */

  4. RES_ERROR, /* 1: R/W Error */

  5. RES_WRPRT, /* 2: Write Protected */

  6. RES_NOTRDY, /* 3: Not Ready */

  7. RES_PARERR /* 4: Invalid Parameter */

  8. } DRESULT;


关于命令参数,分为标准命令参数以及可选命令参数,当 FF_FS_READONLY == 1 和 FF_MAX_SS == FF_MIN_SS时, disk_ioctl 函数不需要实现也可运行,此处我们先简单实现标准命令参数。

标准命令参数有与以下几种:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

以下是可选命令,大家亦可自行查看官方文档,此处不再实现。

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

根据SD nand操作接口,修改实现如下:


  1. /*-----------------------------------------------------------------------*/

  2. /* Miscellaneous Functions                                               */

  3. /*-----------------------------------------------------------------------*/


  4. DRESULT disk_ioctl (

  5.     BYTE pdrv, /* Physical drive nmuber (0..) */

  6.     BYTE cmd, /* Control code */

  7.     void *buff /* Buffer to send/receive control data */

  8. )

  9. {

  10.     switch (cmd) {

  11.         case CTRL_SYNC:

  12.         break;

  13.         case GET_SECTOR_COUNT:

  14.             *(LBA_t *)buff = (SDCardInfo.CardCapacity / SDCardInfo.CardBlockSize);

  15.         break;

  16.         case GET_SECTOR_SIZE:

  17.             *(WORD *)buff = SDCardInfo.CardBlockSize;

  18.         break;

  19.         case GET_BLOCK_SIZE:

  20.             *(DWORD *)buff = 1;

  21.         break;

  22.         case CTRL_TRIM:

  23.         break;

  24.     }


  25.     return RES_OK;

  26. }

5.2.8 修改 ffconf.h 取消 get_fattime 实现

get_fattime()函数用于获取当前时间,此函数不影响fatfs访问sd nand操作,因此我们暂时先屏蔽,后续再实现

进入ffconf.h 文件,修改 FF_FS_NORTC 宏为 1

  1. #define FF_FS_NORTC 1

  2. #define FF_NORTC_MON 5

  3. #define FF_NORTC_MDAY 14

  4. #define FF_NORTC_YEAR 2023

  5. /* The option FF_FS_NORTC switches timestamp feature. If the system does not have

  6. /  an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the

  7. /  timestamp feature. Every object modified by FatFs will have a fixed timestamp

  8. /  defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.

  9. /  To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be

  10. /  added to the project to read current time form real-time clock. FF_NORTC_MON,

  11. /  FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.

  12. /  These options have no effect in read-only configuration (FF_FS_READONLY = 1). */


5.2.9 接口测试

在使用使用通用文件操作接口(指 f_open、f_read、f_write、f_close)访问SD nand之前,我们可以先测试下我们刚刚实现的 diskio.c 内的接口是否可用。因为这是通用文件操作底层所依赖的接口,如果这些接口出问题,通用文件操作接口肯定是跑不起来的!


修改 main 函数如下:

  1. #include "diskio.h"

  2. int main(void)

  3. {

  4.     int res;

  5.     

  6.     bsp_uart_init();

  7.     printf("fatfs test!\r\n");

  8.     

  9.     Memset(Buf_RX, 0x00, Buf_Len);

  10.     Fill_Buffer(Buf_TX, Buf_Len, 0x00);

  11.     

  12.     res = disk_initialize(0);

  13.     printf("disk inital res:%d\r\n", res);

  14.     SD_Info(&SDCardInfo);

  15.     

  16.     res = disk_read(0, Buf_RX, 1, 4);

  17.     printf("disk read res:%d\r\n", res);

  18.     dataShow(Buf_RX, Buf_Len);

  19.     

  20.     res = disk_write(0, Buf_TX, 1, 4);

  21.     printf("disk write res:%d\r\n", res);

  22.     dataShow(Buf_TX, Buf_Len);

  23.     

  24.     res = disk_read(0, Buf_RX, 1, 4);

  25.     printf("disk read2 res:%d\r\n", res);

  26.     dataShow(Buf_RX, Buf_Len);

  27. while(1);

  28. }


5.2.10 调用通用文件操作接口进行读写测试

测试通过,接下来,我们便可调用通用的文件操作接口,通过FatFs完成对SD nand的读写访问了。


在进行读写访问之前,我们首先需要格式化SD nand,这是因为新的SD nand上是没有数据的,也就没有文件系统,因此我们需要先对齐进行格式化。此操作与U盘插入电脑上点击格式化操作类似。

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  1.     // 初始化文件系统

  2.     res = f_mount(&fs, "0:", 1);

  3.     if (res != FR_OK) {

  4.         printf("fatfs mount error! ret = %d\r\n", res);

  5.     }

  6.     

  7.     if(res == FR_NO_FILESYSTEM) {  //FR_NO_FILESYSTEM值为13,表示没有有效的设备

  8.         // 格式化SD卡

  9.         res = f_mkfs("0:", 0, buffer, sizeof(buffer));

  10.         if (res != FR_OK) {

  11.             printf("fatfs mkfs error! ret:%d\r\n", res);

  12.         }


  13.         res = f_mount(NULL, "0:", 1);

  14.         printf("fatfs unmount ret:%d!\r\n", res);

  15.         res = f_mount(&fs, "0:", 1);

  16.         if (res != FR_OK) {

  17.             printf("2: fatfs mount error! ret = %d\r\n", res);

  18.         }

  19.     }


5.2.10.1 f_mount() 函数

f_mount() 函数用于挂载文件系统以及取消卸载文件系统,函数原型如下:

  1. FRESULT f_mount (

  2.   FATFS*       fs,    /* [IN] Filesystem object */

  3.   const TCHAR* path,  /* [IN] Logical drive number */

  4.   BYTE         opt    /* [IN] Initialization option */

  5. );


参数:

  • fs:指向一个文件系统对象指针,当传入为 NULL 时,表示卸载文件系统

  • path:一个非空字符串,内容为特定的逻辑物理设备号。

  • opt:可选项,0:不立即挂载(第一次访问时挂载);1:立即挂载


返回值:

  • FR_OK

  • FR_INVALID_DRIVE

  • FR_DISK_ERR

  • FR_NOT_READY

  • FR_NOT_ENABLED

  • FR_NO_FILESYSTEM


使用 f_mount 即可实现挂载和卸载,当然卸载也可以使用f_unmount函数,原型如下:


  1. FRESULT f_unmount (

  2.   const TCHAR* path   /* [IN] Logical drive number */

  3. );

上述代码,首先调用f_mount挂载设备,当检测到设备内没有文件系统时调用f_mkfs格式化设备。

5.2.10.2 f_mkfs() 函数

f_mkfs函数原型如下:

  1. FRESULT f_mkfs (

  2.   const TCHAR* path,   /* [IN] Logical drive number */

  3.   const MKFS_PARM* opt,/* [IN] Format options */

  4.   void* work,          /* [-]  Working buffer */

  5.   UINT len             /* [IN] Size of working buffer */

  6. );


参数:


  • path:一个非空字符串,内容为特定的逻辑物理设备号。

  • opt:指定格式选项结构MKFS_PARM持有格式选项。如果给出空指针,它会以默认值为函数提供每个选项。该结构有五个成员,顺序如下:

  • BYTE fmt:指定 FAT 类型标志FM_FAT、FM_FAT32、FM_EXFAT和这三者的按位或FM_ANY的组合。未启用 exFAT 时忽略FM_EXFAT 。这些标志指定要创建的 FAT 卷类型。如果指定了两种或多种类型,将根据卷大小和au_size选择其中一种。标志FM_SFD指定以 SFD 格式在驱动器上创建卷。默认值为FM_ANY。

  • BYTE n_fat:指定 FAT/FAT32 卷上的 FAT 副本数。此成员的有效值为 1 或 2。默认值 (0) 和任何无效值都为 1。如果 FAT 类型为 exFAT,则此成员无效。

  • UINT n_align:以扇区为单位指定卷数据区(文件分配池,通常是闪存介质的擦除块边界)的对齐方式。该成员的有效值介于 1 和 32768 之间(包括 2 的幂)。如果给出零(默认值)或任何无效值,该函数将使用 disk_ioctl 函数从较低层获取块大小。

  • DWORD au_size:以字节为单位指定分配单元(簇)的大小。对于 FAT/FAT32 卷,有效值为扇区大小和 128 * 扇区大小(包括在内)之间的 2 的幂,或者对于 exFAT 卷,最大为 16 MB。如果给出零(默认值)或任何无效值,则该函数使用默认分配单元大小,具体取决于卷大小。

  • UINT n_root:指定 FAT 卷上的根目录条目数。此成员的有效值最大为 32768 并与扇区大小/32 对齐。默认值 (0) 和任何无效值都为 512。如果 FAT 类型为 FAT32 或 exFAT,则此成员无效。

  • work:格式化过程中的工作缓冲区指针

  • len:缓冲区大小,单位字节。大小最小为FF_MAX_SS,大量的工作缓冲区减少了向驱动器写入事务的数量,因此格式化过程将很快完成。


返回值:

  • FR_OK

  • FR_DISK_ERR

  • FR_NOT_READY

  • FR_WRITE_PROTECTED

  • FR_INVALID_DRIVE

  • FR_MKFS_ABORTED

  • FR_INVALID_PARAMETER

  • FR_NOT_ENOUGH_CORE

5.2.10.3 读写测试

SD nand格式好了之后,就可以调用通用文件操作函数接口(f_open、f_read、f_write、f_close)对齐进行读写了,修改 main 函数如下:


  1. #include "ff.h"

  2. #include <string.h>


  3. BYTE buffer[FF_MAX_SS];

  4. /**

  5.  * @brief   Main program

  6.  */

  7. int main(void)

  8. {

  9. FATFS fs;

  10.     FIL file;

  11.     FRESULT res;

  12.     

  13.     bsp_uart_init();


  14.     printf("fatfs test!\r\n");

  15.     

  16.     // 初始化文件系统

  17.     res = f_mount(&fs, "0:", 1);

  18.     if (res != FR_OK) {

  19.         printf("fatfs mount error! ret = %d\r\n", res);

  20.     }

  21.     

  22.     if(res == FR_NO_FILESYSTEM) {  //FR_NO_FILESYSTEM值为13,表示没有有效的设备

  23.         // 格式化SD卡

  24.         res = f_mkfs("0:", 0, buffer, sizeof(buffer));

  25.         if (res != FR_OK) {

  26.             printf("fatfs mkfs error! ret:%d\r\n", res);

  27.         }


  28.         res = f_mount(NULL, "0:", 1);

  29.         printf("fatfs unmount ret:%d!\r\n", res);

  30.         res = f_mount(&fs, "0:", 1);

  31.         if (res != FR_OK) {

  32.             printf("2: fatfs mount error! ret = %d\r\n", res);

  33.         }

  34.     }


  35.     // 创建一个新文件

  36.     res = f_open(&file, "test.txt", FA_CREATE_ALWAYS | FA_WRITE);

  37.     if (res != FR_OK) {

  38.         printf("fatfs open error!\r\n");

  39.     }


  40.     // 写入数据到文件

  41.     const char* data = "Hello, FatFs!";

  42.     UINT bytes_written;

  43.     res = f_write(&file, data, strlen(data), &bytes_written);

  44.     if (res != FR_OK) {

  45.         printf("fatfs write error!\r\n");

  46.     }


  47.     // 关闭文件

  48.     res = f_close(&file);

  49.     if (res != FR_OK) {

  50.         printf("fatfs close error!\r\n");

  51.     }


  52.     // 打开文件并读取数据

  53.     res = f_open(&file, "test.txt", FA_READ);

  54.     if (res != FR_OK) {

  55.         printf("2: fatfs open error!\r\n");

  56.     }


  57.     // 读取文件数据

  58.     char read_data[50];

  59.     UINT bytes_read;

  60.     res = f_read(&file, read_data, sizeof(read_data), &bytes_read);

  61.     if (res != FR_OK) {

  62.         printf("2: fatfs read error!\r\n");

  63.     }


  64.     // 关闭文件

  65.     res = f_close(&file);

  66.     if (res != FR_OK) {

  67.         printf("2: fatfs close error!\r\n");

  68.     }


  69.     // 打印读取的数据

  70.     read_data[bytes_read] = '\0';  // 添加字符串结束符

  71.     printf("Read data: %s\n", read_data);

  72.     

  73.     while (1) {

  74.     }

  75. }


测试结果如下:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

6. 结束语

至此,FatFs已经移植完成,需要注意的是,在此次移植过程中,以分享移植流程、思路为主,对于部分函数接口的实现比较粗糙,在实际项目中需要结合本文中对相应接口的描述,对其进行优化以确保产品的稳定性,不过相信那些都不是什么难题。


————————————————


【本文转载自CSDN,作者: 爱出名的狗腿子


  亲爱的卡友们,欢迎光临雷龙官网,如果看完文章之后还是有疑惑或不懂的地方,请联系我们,自己去理解或猜答案是件很累的事,请把最麻烦的事情交给我们来处理,术业有专攻,闻道有先后,深圳市雷龙发展专注存储行业13年,专业提供小容量存储解决方案。

  

  SD NAND,贴片式TF卡,贴片式SD卡

服务电话&邮箱

Tel:13691982107(优先)17727831243

QQ:1302648372 | line@longsto.com

对外联系邮箱:info@longsto.com

投诉:ceo@longsto.com |13923450403

地址:深圳市龙华区民治街道展滔科技大厦B座1907室

首页
产品
资讯
联系