好的资料:
1. FatFS文件系统 (每条指令讲的都很细)-CSDN博客 ;
2. FATFS文件系统详解之系统移植_fatfs移植-CSDN博客 ;
3. STM32移植FatFs文件系统详细步骤说明-CSDN博客 ;
4.基于STM32F407的SDCard读写操作_stm32f407 sd卡-CSDN博客;
基于STM32F407的SDCard读写操作及USB挂载(HAL库)_stm32f407实现usb读卡器功能 sdio dma-CSDN博客
5.STM32CubeMX教程27 SDIO - 读写SD卡_dap仿真器在cubemx6.10版本如何配置-CSDN博客;
6. 官网平台:FatFs - Generic FAT Filesystem Module (elm-chan.org);
有最新的下载包;
基础知识:
卡容量:
1.标准容量卡(SDSC):最大容量为2GB;
2.高容量卡(SDHC):容量大小为2~32GB的卡;
3.扩展容量卡(SDXC):容量大小为32GB~2TB的卡;
此处有坑,买卡的时候价格差异不大,我选择了64GB的卡,属于SDXC;
而stm32f407:Full compliance with SD Memory Card Specifications Version 2.0,也就是说只能支持SDSC、SDHC不支持SDXC,强行插入SDXC卡,访问时会出现:FR_NO_FILESYSTEM错误。
移植FatFs步骤:
可以参看安富莱 STM32-V5 开发板用户手册第39章,39.7节;
其中:(https://blog.csdn.net/z1236464/article/details/129938061)
1) diskio.c和diskio.h是硬件层。
2) ff.c和ff.h是FatFs的文件系统层和文件系统的API层。
FATFS模块在移植的时候,我们一般只需要修改2个文件,即ffconf.h和diskio.c。
FATFS模块的所有配置项都是存放在ffconf.h里面,我们可以通过配置里面的一些选项,来满足自己的需求。diskio.c是硬件层,负责与底层硬件接口适配。
细节
1. (https://blog.csdn.net/z1236464/article/details/129938061)
ffconf.h:FATFS关键配置文件
①_FS_TINY。这个选项在R0.07版本中开始出现,之前的版本都是以独立的C文件出现(FATFS和Tiny FATFS),有了这个选项之后,两者整合在一起了,使用起来更方便。我们使用FATFS,所以把这个选项定义为0即可。
②_FS_READONLY。这个用来配置是不是只读,本章我们需要读写都用,所以这里设置为0即可。
③_USE_STRFUNC。这个用来设置是否支持字符串类操作,比如f_putc,f_puts等,本章我们需要用到,故设置这里为1。
④ _USE_MKFS。这个用来定时是否使能格式化,本章需
要用到,所以设 置这里为1。
⑤ _USE_FASTSEEK。这个用来使能快速定位,我们设置
为1,使能快速定位。
⑥ _USE_LABEL。这个用来设置是否支持磁盘盘符(磁盘名字)读取与设置。我们设置为1,使能,就可以通过相关函数来读取和设置磁盘的名字了。
⑦_CODE_PAGE。这个用于设置语言类型,包括很多选项(见FATFS官网说明),我们这里设置为936,即简体中文(GBK码,需要c936.c文件支持,该文件在option文件夹)。
⑧_USE_LFN。该选项用于设置是否支持长文件名(还需要_CODE_PAGE支持),取值范围为0~3。0,表示不支持长文件名,1~3是支持长文件名,但是存储地方不一样,我们选择使用3,通过ff_memalloc函数来动态分配长文件名的存储区域。
⑨_VOLUMES。用于设置FATFS支持的逻辑设备数目,我们设置为3的话,即支持3个设备(磁盘)。
⑩_MAX_SS。扇区缓冲的最大值,一般设置为512。
2. FATFS移植步骤
①数据类型:在integer.h里面去定义好数据的类型。这里需要了解你用的编译器的数据类型,并根据编译器定义好数据类型。
②配置:通过ffconf.h配置FATFS的相关功能,以满足你的需要。
③函数编写:打开diskio.c,进行底层驱动编写,一般需要编写6个接口函数。
但是实际上:这六个函数对没有需要用户编写。
1)磁盘状态函数 disk_status 实际对应的函数在文件 sd_diskio_dma.c
static DSTATUS SD_CheckStatus(BYTE lun);
获得当前SD卡数据状态:
SD_TRANSFER_OK: No data transfer is acting
SD_TRANSFER_BUSY: Data transfer is acting
2)磁盘初始化函数 disk_initialize 实际对应的函数在文件 sd_diskio_dma.c
DSTATUS SD_initialize(BYTE lun);
此处引出函数BSP_SD_Init()完成TF卡 的SDIO 的初始化,内部会再引出函数:BSP_SD_MspInit(SD_HandleTypeDef *hsd, void *Params) 完成IO的定义。
3)磁盘读函数disk_read 实际对应的函数在文件 sd_diskio_dma.c
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count);
读SD卡扇区(sector)的数据:
/**
* @brief Reads Sector(s)
* @param lun : not used
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
4)磁盘写函数disk_write 实际对应的函数在文件 sd_diskio_dma.c
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count);
写SD卡扇区(sector)的数据:
/**
* @brief Writes Sector(s)
* @param lun : not used
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
5)磁盘I/O控制函数 disk_ioctl 实际对应的函数在文件 sd_diskio_dma.c
DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff);
获得SD卡的一些状态信息,根据不同的cmd,获得不同的状态:
/**
* @brief I/O control operation
* @param lun : not used
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
6)RTC时间获取函数 get_fattime 是一个弱函数, 需要重写;
下面的图表表示了diskio.c文件如何关联到不同的mcu的底层操作,在ff_gen_drv.c(.h)、sd_diskio_dma.c(.h)文件应当都是stm公司提供的,通过关键数据结构:
a. Disk_drvTypeDef(此结构中有盘的对象<每一个盘中都有自己的操作函数,通过另外一个关键数据结构:Diskio_drvTypeDef实现>)定义在ff_gen_drv.h文件中,而在,ff_gen_drv.c文件中实例化了:Disk_drvTypeDef disk = {{0},{0},{0},0}。
在此文件中通过函数:uint8_t FATFS_LinkDriverEx(const Diskio_drvTypeDef *drv, char *path, uint8_t lun),把sd_diskio_dma.c实例化的对象SD_Driver与对象disk,发生关联;
b. Diskio_drvTypeDef 结构体:定义了初始化函数指针、状态函数指针、读disk函数指针、控制函数指针,这些函数在sd_diskio_dma.c具体实现,并且在文件中实例化了结构体:
const Diskio_drvTypeDef SD_Driver。
整理的完整的SD卡stm32底层的驱动图:
FatFs使用
1)sd卡的运行,起始于SD卡的注册:
FATFS_LinkDriver(&SD_Driver, DiskPath);由常量SD_Driver引出下来一些列函数的执行,完成了SD卡控制的GPIO、时钟等一些列的设置;
2)SD卡的操作:
各种有关SD卡的操作(读、写)都在ff.c文件中:
应用接口函数:(见FatFS文件系统函数使用笔记_fatfs网站-CSDN博客)
文件访问
f_open - 打开/创建文件
f_close - 关闭打开的文件
f_read - 从文件中读取数据
f_write - 将数据写入文件
f_lseek - 移动读/写指针,扩展大小
f_truncate - 截断文件大小
f_sync - 刷新缓存数据
f_forward - 将数据转发到流
f_expand - 为文件分配一个连续的块
f_gets - 读取字符串
f_putc - 写一个字符
f_puts - 写一个字符串
f_printf - 写一个格式化的字符串
f_tell - 获取当前读/写指针
f_eof - 测试文件结尾
f_size - 获取大小
f_error - 测试错误
目录访问
f_opendir - 打开一个目录
f_closedir - 关闭一个打开的目录
f_readdir - 读取目录项
f_findfirst - 打开一个目录并读取匹配的第一个项目
f_findnext - 读取下一个匹配的项目
文件和目录管理
f_stat - 检查文件或子目录是否存在
f_unlink - 删除文件或子目录
f_rename - 重命名/移动文件或子目录
f_chmod - 更改文件或子目录的属性
f_utime - 更改文件或子目录的时间戳
f_mkdir - 创建一个子目录
f_chdir - 更改当前目录
f_chdrive - 更改当前驱动器
f_getcwd - 检索当前目录和驱动器
卷管理和系统配置
f_mount - 注册/取消注册卷的工作区
f_mkfs - 在逻辑驱动器上创建 FAT 卷
f_fdisk - 在物理驱动器上创建分区
f_getfree - 获取卷上的可用空间
f_getlabel - 获取卷标
f_setlabel - 设置卷标
f_setcp - 设置活动代码页