目录

  • FATFS介绍
    • 配置流程
      • SDIO_Init()
      • diskio.c的配置
      • malloc()与free()
    • 文件操作函数
    • Simple Demo
    • 注意事项
  • 缓冲队列

FATFS介绍

FAT File System是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平台。它可以嵌入到资源有限的微控制器中,如 8051, PIC, AVR, ARM, Z80, RX等等,不需要做任何修改。

配置流程

SDIO_Init()

SDIO是底层驱动,要想使用FATFS需要配置底层驱动,可以是SD卡,也可以是外部SRAM,当然SD卡也可以试用SPI通讯。根据硬件原理图配置接口以及中断优先级

SD_Error SD_Init(void)
{NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef  GPIO_InitStructure;u8 clkdiv=0;SD_Error errorstatus=SD_OK;  //SDIO IO口初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD,ENABLE);//使能PORTC,PORTD时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO|RCC_AHBPeriph_DMA2,ENABLE);//使能SDIO,DMA2时钟  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;  //PC.8~12 复用输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;       //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //IO口速度为50MHzGPIO_Init(GPIOC, &GPIO_InitStructure);                     //根据设定参数初始化PC.8~12 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;  //PD2 复用输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;       //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //IO口速度为50MHzGPIO_Init(GPIOD, &GPIO_InitStructure);                     //根据设定参数初始化PD2GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;   //PD7 上拉输入GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;         //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //IO口速度为50MHzGPIO_Init(GPIOD, &GPIO_InitStructure);                     //根据设定参数初始化PD7//SDIO外设寄存器设置为默认值                   SDIO_DeInit();NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;           //SDIO中断配置NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    //抢占优先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;                    //子优先级0 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                //使能外部中断通道NVIC_Init(&NVIC_InitStructure);     //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器errorstatus=SD_PowerON();           //SD卡上电if(errorstatus==SD_OK)errorstatus=SD_InitializeCards();           //初始化SD卡    if(errorstatus==SD_OK)errorstatus=SD_GetCardInfo(&SDCardInfo);   //获取卡信息if(errorstatus==SD_OK)errorstatus=SD_SelectDeselect((u32)(SDCardInfo.RCA<<16));//选中SD卡if(errorstatus==SD_OK)errorstatus=SD_EnableWideBusOperation(1);    //4位宽度,如果是MMC卡,则不能用4位模式 if((errorstatus==SD_OK)||(SDIO_MULTIMEDIA_CARD==CardType)){             if(SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V1_1||SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V2_0){clkdiv=SDIO_TRANSFER_CLK_DIV+6;  //V1.1/V2.0卡,设置最高72/12=6Mhz}else clkdiv=SDIO_TRANSFER_CLK_DIV; //SDHC等其他卡,设置最高72/6=12MhzSDIO_Clock_Set(clkdiv);                //设置时钟频率,SDIO时钟计算公式:SDIO_CK时钟=SDIOCLK/[clkdiv+2];其中,SDIOCLK固定为48Mhz //errorstatus=SD_SetDeviceMode(SD_DMA_MODE); //设置为DMA模式errorstatus=SD_SetDeviceMode(SD_POLLING_MODE);   //设置为查询模式}return errorstatus;
}

diskio.c的配置

这个.c文件里主要是一些API函数,需要我们自己把接口补充上,共有以下五个函数:

DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);

下面是具体实现代码

#define SD_CARD   0  //SD卡,卷标为0//获得磁盘状态
DSTATUS disk_status (BYTE pdrv      /* Physical drive nmuber to identify the drive */
)
{ return RES_OK;
}
//初始化磁盘
DSTATUS disk_initialize (BYTE pdrv              /* Physical drive nmuber to identify the drive */
)
{u8 res=0;     switch(pdrv){case SD_CARD://SD卡res=SD_Init();//SD卡初始化 break;default:res=1; }       if(res)return  STA_NOINIT;else return 0; //初始化成功
}
//读扇区
//pdrv:磁盘编号0~9
//*buff:数据接收缓冲首地址
//sector:扇区地址
//count:需要读取的扇区数
DRESULT disk_read (BYTE pdrv,       /* Physical drive nmuber to identify the drive */BYTE *buff,        /* Data buffer to store read data */DWORD sector,   /* Sector address in LBA */UINT count       /* Number of sectors to read */
)
{u8 res=0; if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误            switch(pdrv){case SD_CARD://SD卡res=SD_ReadDisk(buff,sector,count);     while(res)//读出错{SD_Init(); //重新初始化SD卡res=SD_ReadDisk(buff,sector,count);  //printf("sd rd error:%d\r\n",res);}break;default:res=1; }//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值if(res==0x00)return RES_OK;  else return RES_ERROR;
}
//写扇区
//pdrv:磁盘编号0~9
//*buff:发送数据首地址
//sector:扇区地址
//count:需要写入的扇区数
DRESULT disk_write (BYTE pdrv,          /* Physical drive nmuber to identify the drive */const BYTE *buff,  /* Data to be written */DWORD sector,       /* Sector address in LBA */UINT count           /* Number of sectors to write */
)
{u8 res=0;  if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误           switch(pdrv){case SD_CARD://SD卡res=SD_WriteDisk((u8*)buff,sector,count);while(res)//写出错{SD_Init();    //重新初始化SD卡res=SD_WriteDisk((u8*)buff,sector,count);    //printf("sd wr error:%d\r\n",res);}break;default:res=1; }//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值if(res == 0x00)return RES_OK;    else return RES_ERROR;
}
//其他表参数的获得
//pdrv:磁盘编号0~9
//ctrl:控制代码
//*buff:发送/接收缓冲区指针
DRESULT disk_ioctl (BYTE pdrv,      /* Physical drive nmuber (0..) */BYTE cmd,      /* Control code */void *buff        /* Buffer to send/receive control data */
)
{DRESULT res;                                         if(pdrv==SD_CARD)//SD卡{switch(cmd){case CTRL_SYNC:res = RES_OK; break;   case GET_SECTOR_SIZE:*(DWORD*)buff = 512; res = RES_OK;break;     case GET_BLOCK_SIZE:*(WORD*)buff = SDCardInfo.CardBlockSize;res = RES_OK;break;   case GET_SECTOR_COUNT:*(DWORD*)buff = SDCardInfo.CardCapacity/512;res = RES_OK;break;default:res = RES_PARERR;break;}}return res;
}
//获得时间
//User defined function to give a current time to fatfs module      */
//31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
//15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
DWORD get_fattime (void)
{                return 0;
}
//动态分配内存
void *ff_memalloc (UINT size)
{return (void*)mymalloc(SRAMIN,size);
}
//释放内存
void ff_memfree (void* mf)
{myfree(SRAMIN,mf);
}

malloc()与free()

这里有个问题是malloc()与free()不能使用的问题,也有可能是可以使用,但是不能申请大内存,或是其他 BUG,具体原因未知。按照网络上的步骤,添加了<stdlib.h>与<rt_heap.h>,修改.s文件中heap的大小,去除option中的USE MicroLIB之后,理论上是可以使用malloc()与free()的,但是在使用f_open函数后会提示内存不足,具体原因尚未考究,使用别人的mymalloc()与myfree()。
具体实现参照malloc.c

文件操作函数

FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);              /* Open or create a file */
FRESULT f_close (FIL* fp);                                          /* Close an open file object */
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br);           /* Read data from a file */
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw);    /* Write data to a file */
FRESULT f_lseek (FIL* fp, FSIZE_t ofs);                             /* Move file pointer of a file object */
FRESULT f_truncate (FIL* fp);                                       /* Truncate file */
FRESULT f_sync (FIL* fp);                                           /* Flush cached data of a writing file */
FRESULT f_opendir (DIR* dp, const TCHAR* path);                     /* Open a directory */
FRESULT f_closedir (DIR* dp);                                       /* Close an open directory */
FRESULT f_readdir (DIR* dp, FILINFO* fno);                          /* Read a directory item */
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern);   /* Find first file */
FRESULT f_findnext (DIR* dp, FILINFO* fno);                         /* Find next file */
FRESULT f_mkdir (const TCHAR* path);                                /* Create a sub directory */
FRESULT f_unlink (const TCHAR* path);                               /* Delete an existing file or directory */
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new);    /* Rename/Move a file or directory */
FRESULT f_stat (const TCHAR* path, FILINFO* fno);                   /* Get file status */
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask);          /* Change attribute of the file/dir */
FRESULT f_utime (const TCHAR* path, const FILINFO* fno);            /* Change timestamp of the file/dir */
FRESULT f_chdir (const TCHAR* path);                                /* Change current directory */
FRESULT f_chdrive (const TCHAR* path);                              /* Change current drive */
FRESULT f_getcwd (TCHAR* buff, UINT len);                           /* Get current directory */
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn);   /* Get volume label */
FRESULT f_setlabel (const TCHAR* label);                            /* Set volume label */
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt);                  /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);           /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au);              /* Create a file system on the volume */
FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work);         /* Divide a physical drive into some partitions */
int f_putc (TCHAR c, FIL* fp);                                      /* Put a character to the file */
int f_puts (const TCHAR* str, FIL* cp);                             /* Put a string to the file */
int f_printf (FIL* fp, const TCHAR* str, ...);                      /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp);                      /* Get a string from the file */

主要讲一下几个常用的函数:
1、FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);
主要用于挂载硬盘,初始化时调用即可。
2、FRESULT f_mkdir (const TCHAR* path);
主要用于创建文件夹,但是要一级一级创建,不能创建两级文件夹会报错。
3、FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);
主要用于打开文件,fp是文件指针,path是路径,个人认为绝对路径比较好用,虽然占字节较多,但是可以省去操作文件夹的过程,mode为模式,读、写此类。
4、FRESULT f_lseek (FIL* fp, FSIZE_t ofs);
这个函数是移动指针函数,在UI界面我们可以通过操作鼠标移动输入位置,而诸如此类cmd命令没有光标,是通过变量实现的。光标在文件打开时指向最顶端,可以通过f_size()函数获取函数尾部光标,也可以直接给ofs赋值操作光标。
5、FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br);
读文件函数,buff为读取缓冲区,btr为读取长度,br为实际读取长度。
6、FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw);
写文件函数,buff为写缓冲区,btw为写长度,bw为实际写入长度。
7、FRESULT f_sync (FIL* fp);
同步函数,类似于电脑操作的ctrl+s,如果不同步的话可能造成文件丢失。
8、FRESULT f_close (FIL* fp);
关闭文件,同时保存,所以是包含f_sync的操作,但是文件关闭再打开后会将光标移动至初始位置,使用的时候要考虑好是否有再次打开的时候。

Simple Demo

这是一个简易的写TXT文件demo,旨在阐述流程。

FIL file;                                           //文件对象
FATFS fatfs;                                   //逻辑驱动器的工作区
FRESULT res;                             //FRESULT函数公共结果代码
unsigned int counter;
unsigned char write_buffer[25];                 //写文件缓冲区
unsigned char read_buffer[25];                  //读文件缓冲区
char str[] = "0:/test2.txt";
int i = 0;int main()
{   SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组LED_Init();while(SD_Init())//检测不到SD卡{LED2=!LED2;delay_ms(500);                  }                f_mount(&fatfs,"0:",1);      //挂载SD卡 res = f_open(&file,str,FA_READ | FA_WRITE | FA_OPEN_ALWAYS);   while(res!=0);res = f_lseek(&file,0);                                 //移动写指针到文件首while(res!=0);for(i = 0;i < 25;i++){write_buffer[i] = 0x31 + i % 7;}res = f_write(&file,write_buffer,25,&counter);     //将缓冲器中的内容写入源文件while(res!=0);res = f_lseek(&file,0);                              //移动读指针到文件首while(res!=0);res = f_read(&file,read_buffer,25,&counter);while(res!=0);f_close(&file);                                  //关闭源文件while(1){LED1=!LED1;delay_ms(100);   }
}

注意事项

这两个变量应为全局变量,或者静态局部变量,否则会造成存储失败。

FIL file;                                           //文件对象
FATFS fatfs;                                   //逻辑驱动器的工作区

缓冲队列

瞬时数据量可能大于存储速度,所以利用环形队列原理,写了一个环形缓冲,当缓冲满了之后会报错。正常调用存储函数即可,在空闲时调用写入函数。


/*** @brief  环形缓冲读写任务* @param  fatbuf   :环形缓冲* @param  buff[] :数据 需要为字符串* @retval FS文件枚举,只用于判断是否写入成功*/
uint16_t head_index = 0;//头指针,读数据
uint16_t rear_index = 0;//尾指针,写数据
uint8_t circle_buff_write(char *data)//写接口写在SD卡数据写入函数里
{//存入数据格式:data_len(1 byte) + data + /r/n(2 byte),数据长度用于修改hear_index指向位置uint8_t data_len = strlen(data) + 3;//加3是因为要存入数据长度信息、回车符、换行符uint8_t i = 0;//数据缓冲区满了,存储失败if(head_index < rear_index && CIRCLE_BUFF_SIZE - rear_index + head_index - 1< data_len)return 1;else if(head_index > rear_index && head_index - rear_index - 1 < data_len)return 1;fatbuf[rear_index] = data_len;//单次写入数据长度for(i = 0;i < data_len - 3;i++)//复制数据{rear_index = (rear_index + 1) % CIRCLE_BUFF_SIZE;//更新指针fatbuf[rear_index] = data[i];}rear_index = (rear_index + 1) % CIRCLE_BUFF_SIZE;fatbuf[rear_index] = 0x0d;// \rrear_index = (rear_index + 1) % CIRCLE_BUFF_SIZE;fatbuf[rear_index] = 0x0a;// \nrear_index = (rear_index + 1) % CIRCLE_BUFF_SIZE;return 0;
}
uint8_t circle_buff_to_sd_card(void)//读接口
{unsigned int counter = 0;//计数器,用于记录写入数据的个数,但是并没有使用FRESULT res = FR_OK;//用来判断是否写入if(head_index == rear_index)return 0;if(fatbuf[head_index] + head_index > CIRCLE_BUFF_SIZE){//数据存入了零点附近,被分成了两段res += f_write(file,Time_Stamp,22,&counter);//写时间戳res += f_write(file,&fatbuf[(head_index + 1) % CIRCLE_BUFF_SIZE],CIRCLE_BUFF_SIZE - head_index - 1,&counter);//写入前半段数据res += f_write(file,&fatbuf[0],(uint8_t)fatbuf[head_index] - (CIRCLE_BUFF_SIZE - head_index),&counter);//写入后半段数据}else{res += f_write(file,Time_Stamp,22,&counter);//写时间戳res += f_write(file,&fatbuf[(head_index + 1) % CIRCLE_BUFF_SIZE],(uint8_t)fatbuf[head_index] - 1,&counter);//写入数据}if(res == 0){head_index = (head_index + fatbuf[head_index]) % CIRCLE_BUFF_SIZE;//写入成功,更新头指针}return res;
}

基于FATFS的SD卡存储相关推荐

  1. 基于FPGA的数据采集、通讯和存储系统设计(即FPGA+RTL8211千兆以太网+SD卡存储+RTC+Uart+AD7606数模转换+电流放大采集等硬件设计及程序验证)

    本文主要介绍了学生期间自己做的一个小项目,便于学习初期对fpga的整体把握,涉及了很多常见.常用.常考和面试常问的知识点. 可以作为入门后的拓展学习和应对一些找工作的项目面试. 下面对硬件及软件代码进 ...

  2. 新唐NUC980使用记录:U-Boot Linux 编译与烧录(基于SD1位置SD卡)

    文章目录 目的 SD卡分区 U-Boot编译 U-Boot环境变量 Linux编译 默认设置 使用SD卡剩余分区 使用SD卡分区存放rootfs 制作系统镜像 总结 目的 这篇文章中将测试在 NUC9 ...

  3. html音频从10秒播放至30秒,基于Arduino制作SD卡音乐播放器

    一.项目介绍 前面用ATtiny85制作SD卡音乐播放器,本次主要利用Arduino UNO 和SD卡制作音乐播放器.这个播放器不需要添加多余的模块,只需要SD读卡器和Arduino UNO开发板就可 ...

  4. stm32 Fatfs 读写SD卡

    源:stm32 Fatfs 读写SD卡 转载于:https://www.cnblogs.com/LittleTiger/p/4864052.html

  5. STM32CubeMX+SPI+FATFS读写SD卡

    一.软件硬件说明 软件:STM32CubeMX V6.6.1 /KEIL5 V5.29 硬件:正点原子mini开发板,SD卡,通过SPI方式驱动SD卡,用的是SPI1接口 以上内容来源于正点原子min ...

  6. android的SP存储和SD卡存储

    在android中有着很多的存储方式,例如数据库存储,SD卡存储以及SharedPreferences(以下简称SP)等,下面我们来主要的讲解一下SP以及SD卡存储. 首先是SP,SP存储是一个采用K ...

  7. 3.30 haas506 2.0开发教程-example - SD卡存储数据读写

    SD卡存储数据读写 案例说明 数据的写入与读取 串口工具读取数据 接收数据 CSV格式 案例说明 部分设备使用过程中需要保存大量数据到TF卡中,大部分场景拔插TF卡有不太方便. 所以本案例介绍一种使用 ...

  8. 基于FPGA的SD卡音乐播放器之WM8731篇

    基于FPGA的SD卡音乐播放器之WM8731篇 目录 前言 一.I2C驱动模块 二.WM8731寄存器配置模块 三.WM8731时钟生成模块 四.音频发送模块 总结 前言 这个题目是我之前7月初做的一 ...

  9. 关于Android读取SD卡存储的动态申请

    关于Android读取SD卡存储的动态申请 介绍 Android的目录结构 数据的主要存储方式 疑惑 原来的代码:MainActivity.java 修改后代码: 介绍 这篇文章主要关于我学习SD卡的 ...

最新文章

  1. Windows 下用VS2012(Visual Studio 2012)编译librtmp
  2. WinsockExpert+NC抓包上传之拿WEBSHELL
  3. java.lang.NoClassDefFoundError
  4. html选择器_css的9个常用选择器
  5. [html] websocket和http2有什么区别?http2能取代websocket吗?为什么?
  6. Docker CEO Ben Golub:Docker借助开源、天时走向成功
  7. 2018 年,NLP 研究与应用进展到什么水平了?
  8. mysql中的各种函数(日期函数、字符串函数、数学函数...)
  9. SpringMVC 中整合JSON、XML视图二
  10. android webview打印,javascript - 如何在Android Webview中使网站上的打印按钮工作? - 堆栈内存溢出...
  11. AAAI'22 | 基于Profile信息的口语语言理解基准
  12. 基于与非门和多路开关结构的一位全加器实现方法
  13. 身份证地址码码表MySQL
  14. java 通过经纬度计算巨鹿,中国主要城市经纬度查询
  15. java公寓报修管理系统_学生公寓报修管理系统.pdf
  16. fastDFS上传文件过大
  17. 惠普HP OEM XP SP3镜像文件高速下载
  18. Hadoop2.x Yarn作业提交(客户端)
  19. FFmpeg —— 15.示例程序(九):音频编码器(PCM编码为MP3)
  20. 暴雪即将公布《暗黑破坏神3》新职业

热门文章

  1. c++ 11 std::recursive_timed_mutex递归锁定的互斥,并实现有时限锁定
  2. java反射机制之数组转对象
  3. 《苔花如米小,也学牡丹开》和《“精日”分子》观后感 焦帅宾
  4. Android自定义View(一) - View与ViewGroup
  5. 2019.09.21 多校联合训练(普及组)
  6. 【总结整理】已读功能---摘自《馒头商学院》
  7. MFC基础,MFC自绘控件学习总结.---转
  8. linux 组群设置组群密码,Linux 组群账户管理
  9. 从零开始的pytorch小白使用指北
  10. 请结合物联网的应用,解释为什么物联网提供的是行业性、专业性、区域性的服务