FatFs 特点
分离缓冲的FAT结构和每一个文件,适合快速访问多个文件。
支持多个驱动器和分区。
支持FAT12 , FAT16和FAT32 。
支持8.3格式的文件名(LFN不支持)
支持两个分区规则: Fdisk和超级软盘。
优化8/16-bit微控制器。
应用接口
FatFs /微型FatFs模块提供下列功能。
f_mount – 登记或注销一个工作领域
f_open -打开或创建文件
f_close – 关闭一个文件
f_read – 读文件
f_write – 写文件
f_lseek -移动文件读/写指针
f_truncate – 截断文件
f_sync – 刷新缓存的数据
f_opendir – 打开一个目录
f_readdir – 阅读目录项目
f_getfree -获取免费集群
f_stat – 获取文件状态
f_mkdir – 创建一个目录
f_unlink – 删除文件或目录
f_chmod – 更改属性
f_utime – 变更时间戳记
f_rename -重命名/移动文件或目录
f_mkfs – 创建一个文件系统的驱动器
f_forward -转发文件数据流直接
fgets – 读一个字符串
fputc – 写一个字符
fputs – 写一个字符串
fprintf – 写格式化字串
Disk I/O Interface
disk_initialize -初始化的磁盘驱动器
disk_status – 获取磁盘状态
disk_read – 读部门(星期日)
disk_write – 收件部门(星期日)
disk_ioctl – 控制装置依赖功能
get_fattime – 获取当前时间
一种易于移植和使用的文件系统FatFs Moule
引 言
随着信息技术的发展,当今社会的信息量越来越大,以往由单片机构成的系统简单地对存储媒介按地址、按字节的读/写已经不满足人们实际应用的需要,于是利用文件系统对存储媒介进行管理成了今后单片机系统的一个发展方向。目前常用的文件系统主要有微软的FATl2、FATl6、FAT32、NTFS,以及Linux系统下的EXT2、EXT3等。由于微软Windows的广泛应用,在当前的消费类电子产品中,用得最多的还是FAT文件系统,如U盘、MP3、MP4、数码相机等,所以找到一款容易移植和使用、占用硬件资源相对较小而功能又强大的FAT开源文件系统,对于单片机系统设计者来说是很重要的。
FatFs Module是一种完全免费开源的FAT文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C语言编写,所以具有良好的硬件平台独立性,可以移植到8051、PIC、AVR、SH、Z80、H8、ARM等系列单片机上而只需做简单的修改。它支持FATl2、FATl6和FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对8位单片机和16位单片机做了优化。FatFs Module有个简化版本Tiny—FatFs,它跟完全版FatFs的不同之处主要有两点:
①占用内存更少,只要1 KB RAM;
②1次仅支持1个存储介。
1:下载代码fatfs_ff007c,添加到工程,并通过ff.h做相应配置, 我的配置如下
#define _WORD_ACCESS 0
#define _FS_READONLY 0
#define _FS_MINIMIZE 0
#define _FS_TINY 1
...【根据它的注释进行配合即可】
下图就是工程中的结构:mmc_sd.c是自己实现的fatfs文件系统与SD卡驱动的衔接文件。
2:移植fatfs前,系统需包含sd卡的驱动程序,至少需要实现下面几个函数
//SD卡的初始化函数
SD_Init()
//SD卡的卡寄存器读取函数
SD_GetCardInfo(&SDinfo);
//SD卡读取一个扇区的函数【在文件系统中是通过逻辑块与物理块管理文件,但文件系统对硬件设备操作的最小单元是扇区】
SD_ReadBlock(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t BlockSize);
//SD卡读取多个扇区的函数
SD_ReadMultiBlocks
(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
//SD卡写一个扇区的函数
SD_WriteBlock(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t BlockSize)
//SD卡写多个扇区的函数
SD_WriteMultiBlocks
(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
3:fatfs与SD驱动的接口函数
就是下面4个简单的函数,实现了就可以了
DSTATUS MMC_disk_initialize(void)
DRESULT MMC_disk_read(BYTE *buff, DWORD sector, BYTE count)
DRESULT MMC_disk_write(const BYTE *buff, DWORD sector, BYTE count)
DRESULT MMC_disk_ioctl(BYTE ctrl, void *buff)
disk_ioctl:
DRESULT disk_ioctl (
BYTE drv,
BYTE ctrl,
void *buff
)
CPU调用disk_ioctl函数执行ctrl指令对应的动作,这些指令一般都是通过底层flash驱动读取flash存储设备中的一些特殊寄存器(如SD卡中的OCR、CID、RCA等)。通过查找,可以发现在调用该函数前,主调函数先会调用disk_initialize,所以我们可以在disk_initialize中初始化SD接口,并将SD卡中的各个寄存器的值都读取并存储起来,以备disk_ioctl使用。【disk_ioctl该函数网络上有教材说可以直接返回0,那是不行的,因为很多地方用disk_ioctl来获取SD卡的相关信息后,才做出正确的动作,否则出错,比如f_mkfs:格式化操作等】
The RCA register is not available in SPI Mode.
OCR: 卡操作电压寄存器 32位, 只读,每隔0.1V占1位, 第31位卡上电过程是否完成
CID: 卡身份识别寄存器 128bit,只读, 厂家号,产品号,串号,生产日期
SCR: 卡配置寄存器, 可写的 64bit 是否用Security特性(LINUX不支持),以及数据位宽(1bit或4bit)
RCA: 卡地址寄存器: 可写的 16bit SD host和卡进行协商的一个地址,内核中会在代码里面记录这个地址,卡这 边则写到RCA寄存器
CSD: 卡专有数据寄存器 部分可读写 128bit, 卡容量,最大传输速率,读写操作的最大电流、电压,读写擦出块的最大长度等
f_mount:通过文件系统管理一个存储设备,需先在该设备上创建文件系统(先格式化创建文件系统f_mkfs),如果已经创建好了,只需通过f_mount挂载后即可使用。
//////////////////////具体代码的实现//////////////////////////////
DSTATUS MMC_disk_initialize(void)
{
SD_Error err;
if ((err = SD_Init()) != SD_RESPONSE_NO_ERROR) {
return err;
} else {
//USART_SendString(USART2,"MMC_disk_initialize OK!\n");
}
SD_GetCardInfo(&SDinfo);
return RES_OK;
}
DRESULT MMC_disk_read(BYTE *buff, DWORD sector, BYTE count)
{
sector *= 512;
if (count == 1) {
SD_ReadBlock(buff,sector,512);
count = 0;
} else {
SD_ReadMultiBlocks(buff,sector,512,count);
count = 0;
}
return count?RES_ERROR:RES_OK;
}
DRESULT MMC_disk_write(const BYTE *buff, DWORD sector, BYTE count)
{
sector *= 512;
if (count == 1) {
SD_WriteBlock((BYTE *)buff,sector,512);
count = 0;
} else {
SD_WriteMultiBlocks((BYTE *)buff,sector,512,count);
count = 0;
}
return count?RES_ERROR:RES_OK;
}
//可以搜索下下面的各个命令,只有其中的几个被使用
DRESULT MMC_disk_ioctl(BYTE ctrl, void *buff)
{
DRESULT res;
res = RES_ERROR;
switch(ctrl) {
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD *)buff = (DWORD)SDinfo.CardCapacity / 512;
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(DWORD *)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(DWORD *)buff = (DWORD)SDinfo.CardBlockSize;
res = RES_OK;
break;
case CTRL_POWER:
res = RES_OK;
break;
case CTRL_LOCK:
res = RES_OK;
break;
case CTRL_EJECT:
break;
case MMC_GET_TYPE:
break;
case MMC_GET_CSD:
break;
case MMC_GET_CID:
break;
case MMC_GET_OCR:
break;
case MMC_GET_SDSTAT:
break;
}
return res;
}
/////////////////////应用层/////////////////////////
应用层 通过串口显示菜单,打开文件,读写文件,实现人机交互
char * Menu[] = {
"create new file: 1 \n",
"create new dir: 2 \n",
"delete file: 3 \n",
"delete dir: 4 \n",
"open file: 5 \n",
"open dir: 6 \n",
"read file: 7 \n",
"write file: 8 \n",
};
static void AppTaskUsartDisp(void )
{
char err;
char *pString = NULL;
pQ = OSQCreate(MsgQArray,20);
USART_SendString(USART2,"Usart2 is Ok!\n");
USART_SendString(USART2,Menu[0]);
USART_SendString(USART2,Menu[1]);
USART_SendString(USART2,Menu[2]);
USART_SendString(USART2,Menu[3]);
USART_SendString(USART2,Menu[4]);
USART_SendString(USART2,Menu[5]);
USART_SendString(USART2,Menu[6]);
USART_SendString(USART2,Menu[7]);
while(DEF_TRUE) {
pString = (char *)OSQPend(pQ,0,&err);
USART_SendString(USART2,pString);
}
}
void AppOpenfile (void)
{
u8 err;
FATFS fs;
FIL file;
FRESULT res;
char * pBoxData;
pBoxData = (char *)OSMboxPend(pBox,0,&err);
OSQPost(pQ,pBoxData);
res = f_open(&file,pBoxData, FA_OPEN_EXISTING | FA_READ);
if (res == FR_OK) {
OSQPost(pQ,"\n OPen succed!\n");
} else {
OSQPost(pQ,"\n OPen failed!\n");
}
f_close(&file);
}
void AppCreatefile (void)
{
u8 err;
FATFS fs;
FIL file;
FRESULT res;
char * pBoxData;
pBoxData = (char *)OSMboxPend(pBox,0,&err);
OSQPost(pQ,pBoxData);
res = f_open(&file,pBoxData, FA_CREATE_NEW | FA_READ);
if (res == FR_OK) {
OSQPost(pQ,"\n create succed!\n");
} else {
OSQPost(pQ,"\n create failed!\n");
}
f_close(&file);
}
char fileData[30] = {0};
void AppReadfile (void)
{
u8 err;
FATFS fs;
FIL file;
FRESULT res;
char * pBoxData;
u32 br;
pBoxData = (char *)OSMboxPend(pBox,0,&err);
OSQPost(pQ,pBoxData);
res = f_open(&file,pBoxData, FA_OPEN_EXISTING | FA_READ);
if (res == FR_OK) {
OSQPost(pQ,"\n OPen succed!\n");
} else {
OSQPost(pQ,"\n OPen failed!\n");
}
res = f_read(&file,fileData,20,&br);
fileData[21] = '\0';
if (res == FR_OK) {
OSQPost(pQ,"Data:");
OSQPost(pQ,fileData);
} else {
OSQPost(pQ,"\n read failed!\n");
}
f_close(&file);
}
void AppWritefile (void)
{
u8 err;
u8 Len;
FATFS fs;
FIL file;
FRESULT res;
char * pBoxData;
u32 br;
pBoxData = (char *)OSMboxPend(pBox,0,&err);
OSQPost(pQ,pBoxData);
res = f_open(&file,pBoxData, FA_OPEN_EXISTING | FA_READ | FA_WRITE);
if (res == FR_OK) {
OSQPost(pQ,"\n OPen succed!\n");
} else {
OSQPost(pQ,"\n OPen failed!\n");
}
OSQPost(pQ,"Input:");
pBoxData = (char *)OSMboxPend(pBox,0,&err);
Len = strlen(pBoxData);
res = f_write(&file,pBoxData,Len,&br);
if (res == FR_OK) {
OSQPost(pQ,"\n write OK!\n");
} else {
OSQPost(pQ,"\n write failed!\n");
}
f_close(&file);
}
static void AppTaskSdCard(void )
{
u8 err;
char BoxData;
FATFS fs;
FRESULT res;
FIL file;
pBox = OSMboxCreate((void *)0);
res = f_mount(0,&fs);
OSQPost(pQ,"fatfs!\n");
while (1) {
BoxData = (char)OSMboxPend(pBox,0,&err);
switch (BoxData) {
case '1'://create new file
OSQPost(pQ,"new filename:");
AppCreatefile();
break;
case '2':
OSQPost(pQ,"2222!\n");
break;
case '3':
OSQPost(pQ,"3333!\n");
break;
case '4':
OSQPost(pQ,"f4444!\n");
break;
case '5': //Openfile
OSQPost(pQ,"filename:");
AppOpenfile();
break;
case '6':
OSQPost(pQ,"6666!\n");
break;
case '7': //read file:
OSQPost(pQ,"filename:");
AppReadfile();
break;
case '8': //write file
OSQPost(pQ,"filename:\n");
AppWritefile();
break;
default:
OSQPost(pQ,"null!\n");
break;
}
}
}
//返回的是 字符串的字符个数
int GetStringFromFifo(sequeue *sq, datatype *buff)
{
int Count = 0;
do {
*buff = DeQueue(sq);
if (*buff == NULL || *buff == ENDFLAG ) {
break;
}
if (++Count <= BUFFERSIZE - 1) {
buff ++;
}
} while(1);
*(buff++) = '\0';
return Count;
}
datatype InputBuffer[BUFFERSIZE];
static void AppTaskUsartInput (void *p_arg)
{
u8 tick=0 , Cnt = 0 ,err;
NVIC_InitTypeDef NVIC_InitStruct;
USART_InitTypeDef USART_InitStructure;
(void)p_arg;
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQChannel;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
NVIC_Init(&NVIC_InitStruct);
STM_EVAL_COMInit(COM1, &USART_InitStructure);
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
SetNull(&USART_FIFO);
pSem = OSSemCreate(0);
while(DEF_TRUE) {
OSSemPend(pSem,0,&err);
tick++;
//OSTimeDlyHMSM(0,0,0,10);
Cnt = GetStringFromFifo(&USART_FIFO,InputBuffer);
//OSQPost(pQ,InputBuffer);
if (Cnt == 1) {
OSMboxPost(pBox,(void *)InputBuffer[0]);
} else {
OSMboxPost(pBox,(void *)InputBuffer);
}
if(tick&0x01)
{
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_SetBits(GPIOD,GPIO_Pin_4);
GPIO_ResetBits(GPIOD,GPIO_Pin_3);
GPIO_ResetBits(GPIOD,GPIO_Pin_7);
}
else
{
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_4);
GPIO_SetBits(GPIOD,GPIO_Pin_3);
GPIO_SetBits(GPIOD,GPIO_Pin_7);
}
}
}
///////////////测试/////////////////////