STM32移植文件系统FATFS——片外SPI FLASH
一、电路连接
主控芯片选型为:STM32F407ZGT6,SPI FLASH选型为:W25Q256JV。
采用了两片32MB的片外SPI FLASH,电路如图所示。
SPI FLASH与主控芯片的连接方式如表所示。
STM32F407GT6 | W25Q256JV |
---|---|
PB3 | SPI1_SCK |
PB4 | SPI1_MISO |
PB5 | SPI1_MOSI |
PB7 | FLASH_CS1 |
PB8 | FLASH_CS2 |
二、SPI FLASH直接读写
本文采用硬件SPI通信,分为四个文件,分别为:spi.c、spi.h、flash.c、flash.h。
2.1 spi.c源文件
spi.c源文件如下,主要进行spi硬件初始化和收发函数定义。
#include "spi.h"SPI_HandleTypeDef hspi1;static u8 pRx = 0;void SPI_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_SPI1_CLK_ENABLE();GPIO_InitStructure.Pin = SPI1_CLK | SPI1_MISO | SPI1_MOSI;GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;GPIO_InitStructure.Pull = GPIO_PULLUP;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStructure.Alternate = GPIO_AF5_SPI1;HAL_GPIO_Init(SPI1_PORT, &GPIO_InitStructure);hspi1.Instance = SPI1;hspi1.Init.Mode = SPI_MODE_MASTER;hspi1.Init.Direction = SPI_DIRECTION_2LINES;hspi1.Init.DataSize = SPI_DATASIZE_8BIT;hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;hspi1.Init.NSS = SPI_NSS_SOFT;hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;hspi1.Init.TIMode = SPI_TIMODE_DISABLE;hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_ENABLE;hspi1.Init.CRCPolynomial = 7;HAL_SPI_Init(&hspi1);
}u8 SPI1_ReadWriteByte(u8 data)
{HAL_SPI_TransmitReceive(&hspi1, &data, &pRx, 1, 10);return pRx;
}
2.2 spi.h头文件
spi.h头文件如下,主要定义了接口
#ifndef _SPI_H_
#define _SPI_H_#include "system.h"
#include "delay.h"#define SPI1_CLK GPIO_PIN_3
#define SPI1_MISO GPIO_PIN_4
#define SPI1_MOSI GPIO_PIN_5
#define SPI1_PORT GPIOBextern SPI_HandleTypeDef hspi1;extern void SPI_Init(void);
extern u8 SPI1_ReadWriteByte(u8 data);#endif
2.3 flash.c源文件
flash.c源文件如下,主要进行W25Q256JV的硬件初始化和一些设置函数。
#include "flash.h"void W25Q256_Init(uint16_t selectChip)
{GPIO_InitTypeDef GPIO_InitStructure;__HAL_RCC_GPIOB_CLK_ENABLE();GPIO_InitStructure.Pin = selectChip;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStructure.Pull = GPIO_PULLUP;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(FLASH_PORT, &GPIO_InitStructure);FLAS_CS_DISABLE(selectChip);SPI_Init();delay_ms(1);W25Q256_4BDSet(selectChip);
}void W25Q256_4BDSet(uint16_t selectChip)
{u8 Reg3;FLAS_CS_ENABLE(selectChip);Reg3 = SPI1_ReadWriteByte(W25Q256_ReadStatusReg3);SPI1_ReadWriteByte(W25Q256_WriteEnable);SPI1_ReadWriteByte(W25Q256_WriteStatusReg3);SPI1_ReadWriteByte(Reg3 | (1<<2));FLAS_CS_DISABLE(selectChip);delay_us(3);
}u8 W25Q256_Read_SR(uint16_t selectChip, u8 Reg)
{u8 byte = 0;FLAS_CS_ENABLE(selectChip);SPI1_ReadWriteByte(Reg);byte = SPI1_ReadWriteByte(0xff);FLAS_CS_DISABLE(selectChip);return byte;
}void W25Q256_Write_SR(uint16_t selectChip, u8 Reg, u8 sr)
{ FLAS_CS_ENABLE(selectChip);SPI1_ReadWriteByte(Reg);SPI1_ReadWriteByte(sr);FLAS_CS_DISABLE(selectChip);
}void W25Q256_Write_Enable(uint16_t selectChip)
{FLAS_CS_ENABLE(selectChip);SPI1_ReadWriteByte(W25Q256_WriteEnable);FLAS_CS_DISABLE(selectChip);
}void W25Q256_Write_Disable(uint16_t selectChip)
{FLAS_CS_ENABLE(selectChip);SPI1_ReadWriteByte(W25Q256_WriteDisable);FLAS_CS_DISABLE(selectChip);
}u16 W25Q256_ReadID(uint16_t selectChip)
{u16 Temp = 0;FLAS_CS_ENABLE(selectChip);SPI1_ReadWriteByte(W25Q256_ManufactDeviceID);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);Temp |= SPI1_ReadWriteByte(0x00)<<8; Temp |= SPI1_ReadWriteByte(0x00); FLAS_CS_DISABLE(selectChip);return Temp;
}void W25Q256_Read(uint16_t selectChip, u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{ u16 i; FLAS_CS_ENABLE(selectChip); SPI1_ReadWriteByte(W25Q256_ReadData4BA);SPI1_ReadWriteByte((u8)((ReadAddr)>>24)); SPI1_ReadWriteByte((u8)((ReadAddr)>>16)); SPI1_ReadWriteByte((u8)((ReadAddr)>>8)); SPI1_ReadWriteByte((u8)ReadAddr); for (i = 0; i < NumByteToRead; i++){pBuffer[i] = SPI1_ReadWriteByte(0XFF); //循环读数 }FLAS_CS_DISABLE(selectChip);
}static void W25Q256_Write_Page(uint16_t selectChip, u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{u16 i; W25Q256_Write_Enable(selectChip);FLAS_CS_ENABLE(selectChip); SPI1_ReadWriteByte(W25Q256_PageProgram4BA);SPI1_ReadWriteByte((u8)((WriteAddr)>>24));SPI1_ReadWriteByte((u8)((WriteAddr)>>16));SPI1_ReadWriteByte((u8)((WriteAddr)>>8)); SPI1_ReadWriteByte((u8)WriteAddr);for(i = 0;i < NumByteToWrite; i++){SPI1_ReadWriteByte(pBuffer[i]); }FLAS_CS_DISABLE(selectChip);W25Q256_Wait_Busy(selectChip);
}static void W25Q256_Write_NoCheck(uint16_t selectChip, u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{ u16 pageremain; pageremain = 256 - WriteAddr % 256; // 单页剩余的字节数 if(NumByteToWrite <= pageremain){pageremain = NumByteToWrite; // 不大于256个字节}while(1){ W25Q256_Write_Page(selectChip, pBuffer, WriteAddr, pageremain);if(NumByteToWrite == pageremain){break;}else{pBuffer += pageremain;WriteAddr += pageremain; NumByteToWrite -= pageremain;if(NumByteToWrite > 256){pageremain = 256;}else {pageremain = NumByteToWrite;}}}
}u8 W25Q256_BUFFER[4096];
void W25Q256_Write(uint16_t selectChip, u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{ u32 secpos;u16 secoff;u16 secremain; u16 i; u8 *W25Q256_BUF;W25Q256_BUF = W25Q256_BUFFER; secpos = WriteAddr/4096;//扇区地址 secoff = WriteAddr%4096;//在扇区内的偏移secremain = 4096-secoff;//扇区剩余空间大小if(NumByteToWrite <= secremain){secremain = NumByteToWrite;//不大于4096个字节}while(1) { W25Q256_Read(selectChip, W25Q256_BUF, secpos*4096, 4096);//读出整个扇区的内容for(i = 0; i < secremain; i++)//校验数据{if(W25Q256_BUF[secoff+i] != 0XFF){break;} }if(i < secremain) //需要擦除{W25Q256_Erase_Sector(selectChip, secpos); //擦除这个扇区for(i = 0; i < secremain; i++) //复制{W25Q256_BUF[i+secoff] = pBuffer[i]; }W25Q256_Write_NoCheck(selectChip, W25Q256_BUF, secpos*4096, 4096);//写入整个扇区 }else {W25Q256_Write_NoCheck(selectChip, pBuffer, WriteAddr, secremain);//写已经擦除了的,直接写入扇区剩余区间. }if(NumByteToWrite == secremain){break;//写入结束了}else//写入未结束{secpos++;//扇区地址增1secoff = 0;//偏移位置为0 pBuffer += secremain; //指针偏移WriteAddr += secremain;//写地址偏移 NumByteToWrite -= secremain; //字节数递减if(NumByteToWrite > 4096){secremain=4096; //下一个扇区还是写不完}else {secremain = NumByteToWrite; //下一个扇区可以写完了} }}
}void W25Q256_Erase_Chip(uint16_t selectChip)
{ W25Q256_Write_Enable(selectChip);W25Q256_Wait_Busy(selectChip); FLAS_CS_ENABLE(selectChip);SPI1_ReadWriteByte(W25Q256_ChipErase);FLAS_CS_DISABLE(selectChip);W25Q256_Wait_Busy(selectChip);
}void W25Q256_Erase_Sector(uint16_t selectChip, u32 Dst_Addr)
{ Dst_Addr *= 4096;W25Q256_Write_Enable(selectChip);W25Q256_Wait_Busy(selectChip);FLAS_CS_ENABLE(selectChip);SPI1_ReadWriteByte(W25Q256_SectorErase4BA);SPI1_ReadWriteByte((u8)((Dst_Addr)>>24));SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));SPI1_ReadWriteByte((u8)((Dst_Addr)>>8)); SPI1_ReadWriteByte((u8)Dst_Addr); FLAS_CS_DISABLE(selectChip);W25Q256_Wait_Busy(selectChip);
}void W25Q256_Wait_Busy(uint16_t selectChip)
{ while((W25Q256_Read_SR(selectChip, W25Q256_ReadStatusReg1) & 0x01) == 0x01);
}void W25Q256_Power_Down(uint16_t selectChip)
{ FLAS_CS_ENABLE(selectChip);SPI1_ReadWriteByte(W25Q256_PowerDown);FLAS_CS_DISABLE(selectChip);delay_us(3);
} void W25Q256_WAKEUP(uint16_t selectChip)
{ FLAS_CS_ENABLE(selectChip);SPI1_ReadWriteByte(W25Q256_ReleasePowerDown);FLAS_CS_DISABLE(selectChip);delay_us(3);
}
2.4 flash.h头文件
flash.h头文件如下:
#ifndef _FLASH_H_
#define _FLASH_H_#include "system.h"
#include "spi.h"#define FLASH1 GPIO_PIN_7
#define FLASH2 GPIO_PIN_8
#define FLASH_PORT GPIOB#define FLAS_CS_ENABLE(x) HAL_GPIO_WritePin(FLASH_PORT, x, GPIO_PIN_RESET)
#define FLAS_CS_DISABLE(x) HAL_GPIO_WritePin(FLASH_PORT, x, GPIO_PIN_SET)// W25Q256指令集 4字节地址
#define W25Q256_WriteEnable 0x06
#define W25Q256_SRWriteEnable 0x50
#define W25Q256_WriteDisable 0x04
#define W25Q256_ReleasePowerDown 0xAB
#define W25Q256_ManufactDeviceID 0x90
#define W25Q256_JedecDeviceID 0x9F
#define W25Q256_ReadUniqueID 0x4B
#define W25Q256_ReadData 0x03
#define W25Q256_ReadData4BA 0x13
#define W25Q256_FastReadData 0x0B
#define W25Q256_FastReadData4BA 0x0C
#define W25Q256_PageProgram 0x02
#define W25Q256_PageProgram4BA 0x12
#define W25Q256_SectorErase 0x20
#define W25Q256_SectorErase4BA 0x21
#define W25Q256_BlockErase32 0x52
#define W25Q256_BlockErase64 0xD8
#define W25Q256_BlockErase644BA 0xDC
#define W25Q256_ChipErase 0xC7
#define W25Q256_ReadStatusReg1 0x05
#define W25Q256_WriteStatusReg1 0x01
#define W25Q256_ReadStatusReg2 0x35
#define W25Q256_WriteStatusReg2 0x31
#define W25Q256_ReadStatusReg3 0x15
#define W25Q256_WriteStatusReg3 0x11
#define W25Q256_ReadExtAddrReg 0xC8
#define W25Q256_WriteExtAddrReg 0xC5
#define W25Q256_ReadSfdpReg 0x5A
#define W25Q256_EraseSecReg 0x44
#define W25Q256_ProgramSecReg 0x42
#define W25Q256_ReadSecReg 0x48
#define W25Q256_GlobalBlockLock 0x7E
#define W25Q256_GlobalBlockUlock 0x98
#define W25Q256_ReadBlockLock 0x3D
#define W25Q256_IndivBlockLock 0x36
#define W25Q256_IndivBlockUlock 0x39
#define W25Q256_EraProSuspend 0x75
#define W25Q256_RraProResume 0x7A
#define W25Q256_PowerDown 0xB9
#define W25Q256_Enter4BAMode 0xB7
#define W25Q256_Exit4BAMode 0xE9
#define W25Q256_EnableReset 0x66
#define W25Q256_ResetDev 0x99extern void W25Q256_Init(uint16_t selectChip);
extern void W25Q256_4BDSet(uint16_t selectChip);
extern u8 W25Q256_Read_SR(uint16_t selectChip, u8 Reg);
extern void W25Q256_Write_SR(uint16_t selectChip, u8 Reg, u8 sr);
extern void W25Q256_Write_Enable(uint16_t selectChip);
extern void W25Q256_Write_Disable(uint16_t selectChip);
extern u16 W25Q256_ReadID(uint16_t selectChip);
extern void W25Q256_Read(uint16_t selectChip, u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);
extern void W25Q256_Write(uint16_t selectChip, u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite);
extern void W25Q256_Erase_Chip(uint16_t selectChip);
extern void W25Q256_Erase_Sector(uint16_t selectChip, u32 Dst_Addr);
extern void W25Q256_Wait_Busy(uint16_t selectChip);
extern void W25Q256_Power_Down(uint16_t selectChip);
extern void W25Q256_WAKEUP(uint16_t selectChip);#endif
通过这四个文件可以实现对两片flash的读写操作。
三、FATFS文件系统移植
3.1 源码下载
源码下载地址:http://elm-chan.org/fsw/ff/00index_e.html
下载的版本是:R0.15a
3.2 源码目录
FATFS下载解压缩后,如图:
documents文件夹下存放一些帮助文档之类的,可以不用考虑,用到的时候再去百度。
source文件夹下存放FATFS文件系统源码,包括diskio.c、diskio.h、ff.c、ff.h、ffconf.h、ffsystem.c、ffunicode.c,共四个源码和三个头文件。后续需配置只需要修改diskio.c和ffconf.h两个文件即可。
3.3 源码复制到自己的工程
① 将FATFS源码中的七个文件复制到自己的工程文件夹中:
② 将源文件添加至keil工程
③ 添加头文件路径
此时点击编译会报错和警告,需要对源文件的信息进行配置。
3.4 修改diskio.c
3.4.1 添加头文件
spi.h和flash.h为第二章中的两个头文件,定义了与硬件直接交互的代码。delay.h为延时头文件。
3.4.2 定义设备驱动号
将原来代码中的0、1、2三个硬件驱动号删掉定义自己的设备。我有两块SPI FLASH,所以定义了两个设备驱动号。
3.4.3 修改disk_status函数
这个函数是查询设备状态的函数,我们使用flash.c中定义的W25Q256_ReadID函数读W25Q256的设备ID号,如果能正确读取,则系统状态正常。
原来的代码是:
修改后的代码是:
3.4.4 修改disk_initialize函数
这个函数是对设备进行初始化的函数,在代码中调用flash.c中定义的W25Q256_Init函数对设备进行初始化。
原来的代码是:
修改后的代码是:
3.4.5 修改disk_read函数
这个函数是对文件进行读的操作,直接调用flash.c中的W25Q256_Read函数即可。
原来的代码是:
修改后的代码是:(sector和count左移12位的原因分别:LBA_t定义的sector是扇区的逻辑地址,即0,1,2...,通过左移12位(乘4096)获得扇区的物理地址;count是读取多少个字节的内容)
3.4.6 修改disk_write函数
这个函数是对文件进行写操作的函数,直接调用flash.c中的W25Q256_Write函数即可,在flash.c中每次写都会先擦除在写入,所以此处不需要再进行扇区擦除,如果W25Q256_Write函数中未进行擦除操作,则在此处还需进行擦除在写入,否则会写入出错。
原来的代码是:
修改后的代码是:
3.4.7 修改disk_ioctl函数
这个函数是获取设备的一些硬件信息之类的,如果此处有问题可能会导致后续挂载创建文件系统失败。
原来的代码是:
修改后的代码是:(SPI_FLASH1和SPI_FLASH2中处理过程一样,SECTOR_SIZE是扇区大小、SECTOR_COUNT是扇区数量,W25Q256扇区大小是4096,一共有8192个扇区。此处的扇区数量必须填写,开始本人漏掉了这个,后续创建文件系统时一直返回14号错误代码,通过一点一点的打印寻找,才发现在f_mkfs函数中调用disk_ioctl查询扇区数量时一直为0导致的)
3.4.8 添加get_fattime函数
这个函数源代码中未给出,直接编译会导致报错。所以需要手动添加,此函数是为了获取文件读写时间的,如果用了RTC实时时钟可以替换这里的年月日时分秒。
3.4.9 diskio.c完整代码
修改后的完整diskio.c文件如下:
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include "spi.h"
#include "flash.h"
#include "delay.h"#define SPI_FLASH1 0
#define SPI_FLASH2 1#define PAGE_SIZE 256
#define SECTOR_SIZE 4096
#define SECTOR_COUNT 8192 DSTATUS disk_status(BYTE pdrv)
{DSTATUS stat = STA_NOINIT;switch (pdrv){case SPI_FLASH1:if (W25Q256_ReadID(FLASH1) == 0xEF18){stat &= ~STA_NOINIT;}break;case SPI_FLASH2:if (W25Q256_ReadID(FLASH2) == 0xEF18){stat &= ~STA_NOINIT;}break;}return stat;
}DSTATUS disk_initialize(BYTE pdrv)
{DSTATUS stat = STA_NOINIT;switch (pdrv){case SPI_FLASH1:W25Q256_Init(FLASH1);delay_us(200);stat = disk_status(pdrv);break;case SPI_FLASH2:W25Q256_Init(FLASH2);delay_us(200);stat = disk_status(pdrv);break;}return stat;
}// pdrv : Physical drive nmuber to identify the drive
// buff : Data buffer to store read data
// sector: Start sector in LBA
// count : Number of sectors to read
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count)
{DRESULT res = RES_PARERR;switch (pdrv) {case SPI_FLASH1:W25Q256_Read(FLASH1, buff, sector << 12, count << 12);res = RES_OK;break;case SPI_FLASH2:W25Q256_Read(FLASH2, buff, sector << 12, count << 12);res = RES_OK;break;}return res;
}#if FF_FS_READONLY == 0
// pdrv : Physical drive nmuber to identify the drive
// buff : Data to be written
// sector : Start sector in LBA
// count : Number of sectors to write
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count)
{DRESULT res = RES_PARERR;switch (pdrv) {case SPI_FLASH1:W25Q256_Write(FLASH1, (u8 *)buff, sector << 12, count << 12);res = RES_OK;break;case SPI_FLASH2:W25Q256_Write(FLASH2, (u8 *)buff, sector << 12, count << 12);res = RES_OK;break;}return res;
}#endif// pdrv : Physical drive nmuber
// cmd : Control code
// buff : Buffer to send/receive control data
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff)
{DRESULT res = RES_PARERR;switch (pdrv) {case SPI_FLASH1:switch (cmd){case CTRL_SYNC:break;case CTRL_TRIM:break;case GET_BLOCK_SIZE:break;case GET_SECTOR_SIZE:*(DWORD*)buff = SECTOR_SIZE;break;case GET_SECTOR_COUNT:*(DWORD*)buff = SECTOR_COUNT;break;default:res = RES_PARERR;break;}res = RES_OK;case SPI_FLASH2:switch (cmd){case CTRL_SYNC:break;case CTRL_TRIM:break;case GET_BLOCK_SIZE:break;case GET_SECTOR_SIZE:*(DWORD*)buff = SECTOR_SIZE;break;case GET_SECTOR_COUNT:*(DWORD*)buff = SECTOR_COUNT;break;default:res = RES_PARERR;break;}res = RES_OK;}return res;
}__weak DWORD get_fattime(void) // 获取时间
{return ((DWORD)(2024-1980)<<25) // 设置年份为2024| ((DWORD)1<<21) // 设置月份为1| ((DWORD)1<<16) // 设置日期为1| ((DWORD)1<<11) // 设置小时为1| ((DWORD)1<<5) // 设置分钟为1| ((DWORD)1<<1); // 设置秒数为1
}
3.5 修改ffconf.h
修改宏定义FF_USE_MKFS:(作用:创建文件系统函数,定义后才能创建文件系统)
修改宏定义FF_CODE_PAGE:(作用:文件语言,设置为简体中文)
修改宏定义FF_VOLUMES:(作用:硬件系统数量,我这挂了两个spi flash,所以是2)
修改宏定义FF_MIN_SS和FF_MAX_SS:(作用:配置扇区最小和最大空间,W25Q256的扇区大小是4096)
完整的ffconf.h文件如下:
/*---------------------------------------------------------------------------/
/ Configurations of FatFs Module
/---------------------------------------------------------------------------*/#define FFCONF_DEF 5380 /* Revision ID *//*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/#define FF_FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ and optional writing functions as well. */#define FF_FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: Basic functions are fully enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/ are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */#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 1
/* This option switches f_mkfs(). (0:Disable or 1:Enable) */#define FF_USE_FASTSEEK 0
/* This option switches fast seek feature. (0:Disable or 1:Enable) */#define FF_USE_EXPAND 0
/* This option switches f_expand(). (0:Disable or 1:Enable) */#define FF_USE_CHMOD 0
/* This option switches attribute control API functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */#define FF_USE_LABEL 0
/* This option switches volume label API functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */#define FF_USE_FORWARD 0
/* This option switches f_forward(). (0:Disable or 1:Enable) */#define FF_USE_STRFUNC 0
#define FF_PRINT_LLI 0
#define FF_PRINT_FLOAT 0
#define FF_STRF_ENCODE 3
/* FF_USE_STRFUNC switches the string API functions, f_gets(), f_putc(), f_puts()
/ and f_printf().
/
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/ 1: Enable without LF - CRLF conversion.
/ 2: Enable with LF - CRLF conversion.
/
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
/ makes f_printf() support floating point argument. These features want C99 or later.
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string API functions convert the character
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/ to be read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*//*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/#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
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 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_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.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN feature
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/ be in range of 12 to 255. It is recommended to be set 255 to fully support the LFN
/ specification.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */#define FF_LFN_UNICODE 0
/* This option switches the character encoding on the API when LFN is enabled.
/
/ 0: ANSI/OEM in current CP (TCHAR = char)
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
/ 2: Unicode in UTF-8 (TCHAR = char)
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
/
/ Also behavior of string I/O functions will be affected by this option.
/ When LFN is not enabled, this option has no effect. */#define FF_LFN_BUF 255
#define FF_SFN_BUF 12
/* This set of options defines size of file name members in the FILINFO structure
/ which is used to read out directory items. These values should be suffcient for
/ the file names to read. The maximum possible length of the read file name depends
/ on character encoding. When LFN is not enabled, these options have no effect. */#define FF_FS_RPATH 0
/* This option configures support for relative path.
/
/ 0: Disable relative path and remove related API functions.
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() is available in addition to 1.
*//*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/#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.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/ logical drive. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table is needed as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/#define FF_MULTI_PARTITION 0
/* This option switches support for multiple volumes on the physical drive.
/ By default (0), each logical drive number is bound to the same physical drive
/ number and only an FAT volume found on the physical drive will be mounted.
/ When this feature is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ will be available. */#define FF_MIN_SS 4096
#define FF_MAX_SS 4096
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk, but a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is
/ configured for variable sector size mode and disk_ioctl() needs to implement
/ GET_SECTOR_SIZE command. */#define FF_LBA64 0
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */#define FF_MIN_GPT 0x10000000
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs() and
/ f_fdisk(). 2^32 sectors maximum. This option has no effect when FF_LBA64 == 0. */#define FF_USE_TRIM 0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/ To enable this feature, also CTRL_TRIM command should be implemented to
/ the disk_ioctl(). *//*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/#define FF_FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */#define FF_FS_EXFAT 0
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */#define FF_FS_NORTC 0
#define FF_NORTC_MON 11
#define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2024
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() need to be added
/ to the project to read current time form real-time clock. FF_NORTC_MON,
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() at the first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/#define FF_FS_LOCK 0
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/ is 1.
/
/ 0: Disable file lock function. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock function. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock control is independent of re-entrancy. */#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk(), are always not re-entrant. Only file/directory access to
/ the same volume is under control of this featuer.
/
/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give(),
/ must be added to the project. Samples are available in ffsystem.c.
/
/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
*//*--- End of configuration options ---*/
四、文件系统测试
在主函数中只需要包含ff.h文件即可,完整的主程序如下:
#include <stdio.h>
#include "system.h"
#include "delay.h"
#include "uart.h"
#include "flash.h"
#include "ff.h"// 文件系统变量
FATFS fs;
FIL fp;
FRESULT fres;
UINT fnum;// 文件读写变量
BYTE buffer[4096] = {0};
BYTE textBuffer[] = "ABCDEFG";
uint8_t c[256] = {0};int main(void)
{HAL_Init();SystemClock_Config();Uart_Init(115200);fres = f_mount(&fs, "1:", 1); // 挂载文件系统if(fres == FR_NO_FILESYSTEM) // 检测是否存在文件系统{fres = f_mkfs("1:", NULL, buffer, 4096); // 创建文件系统if(fres == FR_OK) // 判断是否创建成功{printf("FATFS has been mkf\n");fres = f_mount(NULL, "1:", 0); // 卸载文件系统fres = f_mount(&fs, "1:", 1); // 重新挂载文件系统}else // 创建失败{printf("FATFS mkf filed: %d\n", fres);while(1) // 死循环{} }}else if(fres != FR_OK) // 挂载失败{printf("mount ERROR:%d\n", fres);while(1) // 死循环{}}else // 挂载成功{printf("mount OK\n");}fres = f_open(&fp, "1:ABC.txt", FA_CREATE_ALWAYS | FA_WRITE); // 创建文件 if(fres == FR_OK) // 判断是否创建成功{printf("File open is OK\n");}fres = f_write(&fp, "ABCDEFG", 7, &fnum); // 写入数据if(fres == FR_OK) // 判断是否写入成功{printf("File write is OK\n");}else // 写入失败{printf("%d\n", fres);}f_close(&fp); // 关闭文件if(fres == FR_OK) // 判断是否关闭成功{printf("File close is OK\n");}else // 关闭失败{printf("%d\n", fres);}fres = f_unmount("1:"); // 卸载文件系统 fres = f_mount(&fs,"1:",1); // 重新挂载文件系统fres = f_open(&fp, "1:ABC.txt", FA_OPEN_EXISTING | FA_READ); // 打开文件if(fres == FR_OK) // 判断是否打开成功{printf("File open is OK\n");}else // 打开失败{printf("%d\n", fres);}fres = f_read(&fp, c, 7, &fnum); // 读取文件内容if(fres == FR_OK) // 判断是否读取成功{printf("File read is OK\n");printf("%s\n", c);}else // 读取失败{printf("%d\n", fres);}f_close(&fp); // 关闭文件fres = f_unmount("1:"); // 卸载文件系统if(fres == FR_OK) // 判断是否卸载成功{printf("unmount OK\n");}while (1){ delay_ms(500);}
}
测试结果:
完整工程链接: https://pan.baidu.com/s/1YCRDXtLZMiMOpGDCTqMhLQ?pwd=ccvg
提取码: ccvg