当前位置:网站首页>【FatFs】基于STM32 SD卡移植FatFs文件系统
【FatFs】基于STM32 SD卡移植FatFs文件系统
2022-07-22 09:07:00 【ZHONGCAI0901】
相关文章
《【SDIO】SDIO、SD卡、FatFs文件系统相关文章索引》
1.前言
FatFs是一个通用的FAT/exFAT文件系统模块,用于小型嵌入式系统。它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质。因此它是独立于平台的,可以集成到资源有限的小型微控制器中,如8051、PIC、AVR、ARM、Z80、RX等。FatFs 支持 FAT12、FAT16、FAT32、exFAT 等格式,所以我们利用前面写好的 SD卡驱动,把 FatFs 文件系统代码移植到工程。
2.如何下载FatFs文件系统
FatFs 文件系统的源码可以从 fatfs 官网下载:
http://elm-chan.org/fsw/ff/00index_e.html
下面红色框标记出来的就是FatFs文件系统源码和FatFs样例工程,如下:
下载如下:
3.FatFs文件系统的组织架构
在移植之前,我们需要对FatFs文件系统有一个大致认识。这样我们才知道哪些是可以直接移植,哪些是需要我们自己来实现的。下面显示的依赖关系图是带有FatFs模块的嵌入式系统的典型配置。绿色部分(Low Level Disk I/O Layer)FatFs文件系统不关心具体的实现,所以需要我们自己来实现它。
下面展示了单驱动系统和多驱动系统,我们在实现了SD卡驱动之后,还需要实现一个实时时钟(RTC),它会在创建或者修改文件时会用到。如果需要同时支持U盘、SD卡、Nand Flash等,需要分别实现对应的驱动,然后挂载在不同的节点,这样FatFs就可以同时管理多个设备。
下表显示了根据配置选项需要哪些功能,如下:
Function | Required when | Note |
disk_status disk_initialize disk_read | Always | Disk I/O functions. Samples available in ffsample.zip. There are many implementations on the web. |
disk_write get_fattime disk_ioctl (CTRL_SYNC) | FF_FS_READONLY == 0 | |
disk_ioctl (GET_SECTOR_COUNT) disk_ioctl (GET_BLOCK_SIZE) | FF_USE_MKFS == 1 | |
disk_ioctl (GET_SECTOR_SIZE) | FF_MAX_SS != FF_MIN_SS | |
disk_ioctl (CTRL_TRIM) | FF_USE_TRIM == 1 | |
ff_uni2oem ff_oem2uni ff_wtoupper | FF_USE_LFN != 0 | Unicode support functions. Add optional module ffunicode.c to the project. |
ff_cre_syncobj ff_del_syncobj ff_req_grant ff_rel_grant | FF_FS_REENTRANT == 1 | O/S dependent functions. Sample code is available in ffsystem.c. |
ff_mem_alloc ff_mem_free | FF_USE_LFN == 3 |
4.FatFs文件系统移植步骤
前面我们有实现基于STM32 SDIO SD卡读写测试程序,为移植 FatFs 方便,我们在该工程基础上添加 FatFs 组件,并修改 main 函数的用户程序即可。
- 将 FatFs 组件文件添加到工程中,视图如下:
2.将 FatFs 组件相关的头文件添加到工程,如下:
3.修改diskio.c
文件,主要是将SD卡的初始化、读写操作和实时时钟RTC,添加到对应的位置。(备注:实时时钟RTC驱动未真正的实现,只是使用了固定假的时钟。)
#include "main.h"
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include "bsp_sdio_sd.h"
/* 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 */
#define SD_BLOCKSIZE 512
static volatile DSTATUS currStat = STA_NOINIT; /* Physical drive status */
extern SD_CardInfo SDCardInfo;
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat = STA_NOINIT;
//int result;
switch (pdrv) {
case DEV_RAM :
break;
case DEV_MMC :
stat = currStat;
break;
case DEV_USB :
break;
}
return stat;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat = STA_NOINIT;
switch (pdrv) {
case DEV_RAM :
break;
case DEV_MMC :
if(SD_Init()==SD_OK)
{
stat &= ~STA_NOINIT;
}
else
{
stat = STA_NOINIT;
}
currStat = stat;
break;
case DEV_USB :
break;
}
return stat;
}
/*-----------------------------------------------------------------------*/
/* 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 */
)
{
DRESULT res = RES_PARERR;
SD_Error SD_state = SD_OK;
if (!count) return RES_PARERR; /* Check parameter */
if (currStat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
switch (pdrv) {
case DEV_RAM :
break;
case DEV_MMC :
if(count == 1){
SD_state = SD_ReadBlock(buff, sector*SD_BLOCKSIZE, SD_BLOCKSIZE);
}else{
SD_state=SD_ReadMultiBlocks(buff,sector*SD_BLOCKSIZE,SD_BLOCKSIZE,count);
}
if(SD_state==SD_OK)
{
/* Check if the Transfer is finished */
SD_state=SD_WaitReadOperation();
/* Wait until end of DMA transfer */
while(SD_GetStatus() != SD_TRANSFER_OK);
}
if(SD_state!=SD_OK)
res = RES_PARERR;
else
res = RES_OK;
break;
case DEV_USB :
break;
}
return res;
}
/*-----------------------------------------------------------------------*/
/* 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 */
)
{
DRESULT res = RES_PARERR;
SD_Error SD_state = SD_OK;
if (!count) return RES_PARERR; /* Check parameter */
if (currStat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
if (currStat & STA_PROTECT) return RES_WRPRT; /* Check write protect */
switch (pdrv) {
case DEV_RAM :
break;
case DEV_MMC :
if(count == 1){
SD_state = SD_WriteBlock((uint8_t *)buff, sector*SD_BLOCKSIZE, SD_BLOCKSIZE);
}else{
SD_state=SD_WriteMultiBlocks((uint8_t *)buff,sector*SD_BLOCKSIZE,SD_BLOCKSIZE,count);
}
if(SD_state==SD_OK)
{
/* Check if the Transfer is finished */
SD_state=SD_WaitWriteOperation();
/* Wait until end of DMA transfer */
while(SD_GetStatus() != SD_TRANSFER_OK);
}
if(SD_state!=SD_OK)
res = RES_PARERR;
else
res = RES_OK;
break;
case DEV_USB :
break;
}
return res;
}
#endif
/*-----------------------------------------------------------------------*/
/* 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_PARERR;
//int result;
switch (pdrv) {
case DEV_RAM :
break;
case DEV_MMC :
switch (cmd)
{
// Get R/W sector size (WORD)
case GET_SECTOR_SIZE :
*(WORD * )buff = SDCardInfo.CardBlockSize;
break;
// Get erase block size in unit of sector (DWORD)
case GET_BLOCK_SIZE :
*(DWORD * )buff = SDCardInfo.CardBlockSize;
break;
case GET_SECTOR_COUNT:
*(DWORD * )buff = SDCardInfo.CardCapacity/SDCardInfo.CardBlockSize;
break;
case CTRL_SYNC :
break;
}
res = RES_OK;
break;
case DEV_USB :
break;
}
return res;
}
__weak DWORD get_fattime(void) {
/* 返回当前时间戳 */
return ((DWORD)(2021 - 1980) << 25) /* Year 2021 */
| ((DWORD)1 << 21) /* Month 1 */
| ((DWORD)1 << 16) /* Mday 1 */
| ((DWORD)0 << 11) /* Hour 0 */
| ((DWORD)0 << 5) /* Min 0 */
| ((DWORD)0 >> 1); /* Sec 0 */
}
- 修改
ffconf.h
文件,配置FatFs可能使用到的属性,如下:
--- G:2.stm32f429_sdio_fatfs_test\User\fatfs\ffconf.h
+++ G:2.stm32f429_sdio_fatfs_test\User\fatfs\ffconf.h
@@ -35,13 +35,13 @@
#define FF_USE_FIND 0
/* This option switches filtered directory read functions, f_findfirst() and / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
-#define FF_USE_MKFS 0
+#define FF_USE_MKFS 1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define FF_USE_FASTSEEK 0
/* This option switches fast seek function. (0:Disable or 1:Enable) */
@@ -65,13 +65,13 @@
/*---------------------------------------------------------------------------/ / Locale and Namespace Configurations /---------------------------------------------------------------------------*/
-#define FF_CODE_PAGE 932
+#define FF_CODE_PAGE 936
/* This option specifies the OEM code page to be used on the target system. / Incorrect code page setting can cause a file open failure. / / 437 - U.S. / 720 - Arabic / 737 - Greek @@ -94,13 +94,13 @@ / 949 - Korean (DBCS) / 950 - Traditional Chinese (DBCS) / 0 - Include all code pages above and configured by f_setcp() */
-#define FF_USE_LFN 0
+#define FF_USE_LFN 2
#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. @@ -160,13 +160,13 @@ /*---------------------------------------------------------------------------/ / Drive/Volume Configurations /---------------------------------------------------------------------------*/
-#define FF_VOLUMES 1
+#define FF_VOLUMES 2
/* Number of volumes (logical drives) to be used. (1-10) */
#define FF_STR_VOLUME_ID 0
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
- 修改
main.c
文件,对SD卡进行FatFs测试,如下:
static void put_rc (FRESULT rc)
{
const char *str =
"OK\0" "DISK_ERR\0" "INT_ERR\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0"
"INVALID_NAME\0" "DENIED\0" "EXIST\0" "INVALID_OBJECT\0" "WRITE_PROTECTED\0"
"INVALID_DRIVE\0" "NOT_ENABLED\0" "NO_FILE_SYSTEM\0" "MKFS_ABORTED\0" "TIMEOUT\0"
"LOCKED\0" "NOT_ENOUGH_CORE\0" "TOO_MANY_OPEN_FILES\0" "INVALID_PARAMETER\0";
FRESULT i;
for (i = FR_OK; i != rc && *str; i++) {
while (*str++) ;
}
printf("rc=%u FR_%s\n", (UINT)rc, str);
}
static void fatfs_show_volume_status(char *ptr)
{
FRESULT res;
long p1;
FATFS *fs;
static const char *ft[] = {
"", "FAT12", "FAT16", "FAT32", "exFAT"};
if(ptr == NULL){
return;
}
res = f_getfree("1:/", (DWORD*)&p1, &fs);
if (res) {
put_rc(res); return; }
printf("FAT type = %s\r\n", ft[fs->fs_type]);
printf("Bytes/Cluster = %lu\r\n", (DWORD)fs->csize * 512);
printf("Number of FATs = %u\r\n", fs->n_fats);
if (fs->fs_type < FS_FAT32) printf("Root DIR entries = %u\r\n", fs->n_rootdir);
printf("Sectors/FAT = %lu\r\n", fs->fsize);
printf("Number of clusters = %lu\r\n", (DWORD)fs->n_fatent - 2);
printf("Volume start (lba) = %lu\r\n", fs->volbase);
printf("FAT start (lba) = %lu\r\n", fs->fatbase);
printf("DIR start (lba,clustor) = %lu\r\n", fs->dirbase);
printf("Data start (lba) = %lu\r\n\r\n", fs->database);
}
static void fatfs_directory_listing(char *ptr)
{
FRESULT res;
UINT acc_files, acc_dirs;
QWORD acc_size;
DWORD dw;
FATFS *fs;
if(ptr == NULL){
return;
}
while (*ptr == ' ') ptr++;
res = f_opendir(&Dir, ptr);
if (res) {
put_rc(res); return; }
acc_size = acc_dirs = acc_files = 0;
for(;;) {
res = f_readdir(&Dir, &Finfo);
if ((res != FR_OK) || !Finfo.fname[0]) break;
if (Finfo.fattrib & AM_DIR) {
acc_dirs++;
} else {
acc_files++; acc_size += Finfo.fsize;
}
printf("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\r\n",
(Finfo.fattrib & AM_DIR) ? 'D' : '-',
(Finfo.fattrib & AM_RDO) ? 'R' : '-',
(Finfo.fattrib & AM_HID) ? 'H' : '-',
(Finfo.fattrib & AM_SYS) ? 'S' : '-',
(Finfo.fattrib & AM_ARC) ? 'A' : '-',
(Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15, Finfo.fdate & 31,
(Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63,
Finfo.fsize, Finfo.fname);
}
printf("%4u File(s),%10llu bytes total\r\n%4u Dir(s)", acc_files, acc_size, acc_dirs);
res = f_getfree(ptr, &dw, &fs);
if (res == FR_OK) {
printf(", %10llu bytes free\r\n", (QWORD)dw * fs->csize * 512);
} else {
put_rc(res);
}
}
int main(void)
{
/* 禁用WiFi模块 */
BL8782_PDN_INIT();
// init led
LED_GPIO_Config();
LED1(OFF);
LED2(OFF);
LED3(OFF);
// init uart
Debug_USART1_Config();
printf("starting up!!!\r\n");
// init systick
SysTick_Init();
printf("\r\n****** 这是一个SD卡 文件系统实验 ******\r\n");
//在外部SPI Flash挂载文件系统,文件系统挂载时会对SPI设备初始化
res_sd = f_mount(&fs,"1:",1);
/*----------------------- 格式化测试 ---------------------------*/
/* 如果没有文件系统就格式化创建创建文件系统 */
if(res_sd == FR_NO_FILESYSTEM)
{
printf("》SD卡还没有文件系统,即将进行格式化...\r\n");
/* 格式化 */
res_sd=f_mkfs("1:",0,work, sizeof(work));
if(res_sd == FR_OK)
{
printf("》SD卡已成功格式化文件系统。\r\n");
/* 格式化后,先取消挂载 */
res_sd = f_mount(NULL,"1:",1);
/* 重新挂载 */
res_sd = f_mount(&fs,"1:",1);
}
else
{
LED1(ON);
printf("《《格式化失败。》》\r\n");
while(1);
}
}
else if(res_sd!=FR_OK)
{
printf("!!SD卡挂载文件系统失败。(%d)\r\n",res_sd);
printf("!!可能原因:SD卡初始化不成功。\r\n");
while(1);
}
else
{
printf("》文件系统挂载成功,可以进行读写测试\r\n");
}
fatfs_show_volume_status("1:/");
fatfs_directory_listing("1:/");
/*----------------------- 文件系统测试:写测试 -----------------------------*/
/* 打开文件,如果文件不存在则创建它 */
printf("\r\n****** 即将进行文件写入测试... ******\r\n");
res_sd = f_open(&fnew, "1:FatFs读写测试文件.txt",FA_CREATE_ALWAYS | FA_WRITE );
if ( res_sd == FR_OK )
{
printf("》打开/创建FatFs读写测试文件.txt文件成功,向文件写入数据。\r\n");
/* 将指定存储区内容写入到文件内 */
res_sd=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
if(res_sd==FR_OK)
{
printf("》文件写入成功,写入字节数据:%d\r\n",fnum);
printf("》向文件写入的数据为:\r\n%s\r\n",WriteBuffer);
}
else
{
printf("!!文件写入失败:(%d)\n",res_sd);
}
/* 不再读写,关闭文件 */
f_close(&fnew);
}
else
{
LED1(ON);
printf("!!打开/创建文件失败。\r\n");
}
/*------------------- 文件系统测试:读测试 ------------------------------------*/
printf("****** 即将进行文件读取测试... ******\r\n");
res_sd = f_open(&fnew, "1:FatFs读写测试文件.txt", FA_OPEN_EXISTING | FA_READ);
if(res_sd == FR_OK)
{
LED2(ON);
printf("》打开文件成功。\r\n");
res_sd = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
if(res_sd==FR_OK)
{
printf("》文件读取成功,读到字节数据:%d\r\n",fnum);
printf("》读取得的文件数据为:\r\n%s \r\n", ReadBuffer);
}
else
{
printf("!!文件读取失败:(%d)\n",res_sd);
}
}
else
{
LED1(ON);
printf("!!打开文件失败。\r\n");
}
/* 不再读写,关闭文件 */
f_close(&fnew);
/* 不再使用文件系统,取消挂载文件系统 */
f_mount(NULL,"1:",1);
/* 操作完成,停机 */
while(1)
{
}
//return 0;
}
5.验证测试结果
验证测试结果成功:
6.参考资料
移植成功的完整代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/16531823
边栏推荐
猜你喜欢
PHP开发中csrf攻击的简单演示和防范
Fcntl function
别让恐婚,扼杀你幸福!
【STM32】STM32 SDIO SD卡读写测试(四)-- SD_Test之Transfer Mode阶段
枚举对象中属性
It took two hours to find the bug about scrollto scrolling the distance from offsettop to the top
MySQL优化之强制索引的使用
Using pypyodbc in Ubuntu cannot connect to SQL Server
Understanding of continue in C language (fishing_1)
Fabric.js 控制元素层级
随机推荐
GeoWebCache发布ArcGIS切片数据
PHP实现删除一维数组中某一个值
力扣解法汇总498-对角线遍历
原型对象
double类型不能进行精确计算的问题
第十二讲 MySQL之高可用组件MHA
2.树莓派系统备份
2021-10-18 burn bare board program with EOP
【STM32】STM32 SDIO SD卡读写测试(一)-- SD卡硬件设计和软件移植
creating vlan over openstack (by quqi99)
Js高级-词法作用域
2022-07-21: given a string STR and a positive number k, you can divide STR into multiple substrings at will, in order to find that in a certain division scheme, there are as many palindrome substrings
【STM32】STM32 SDIO SD卡读写测试(二)-- SD_Init之Power On阶段
Fabric. JS centered element
Codeforces Round #806 (Div. 4)(7/7)
别让恐婚,扼杀你幸福!
Lesson 12 MySQL high availability component MHA
centos7.5下添加gd库然后mysql拓展库没了mysql拓展的配置也没问题,phpinfo中就是没有mysql拓展
构造函数
包装类和字符串的方法