目录
一、FatFs简介
FatFs是免费,开源的轻量级 文件系统,纯c实现,移植简单
二、源码下载
GitHub - abbrev/fatfs: FatFs - Generic FAT File System Module
三、移植条件
1、芯片参数
note:如遇挂载的失败,裸机请记得调整栈空间。
芯片类型:STM32F103VET6
flash大小为512KB,RAM大小 64KB
2、flash规划
flash 前62KB存放代码,剩下的450KB由文件系统管理
四、源码结构分析
用户移植适配的工作全部在diskio.c中完成。
五、配置文件重要参数说明
1、配置扇区大小
#define FF_MIN_SS 2048
#define FF_MAX_SS 2048
如果只需要挂载一个文件系统,以上两个值设置一样即可,本例中所用STM32 扇区大小为2048,所以均设置为2048。
如果需要挂载一个以上文件系统且两个设备扇区大小不同,以上两值设置范围要涵盖多个设备扇区范围。disk_ioctl接口需要提供GET_SECTOR_SIZE接口。
2、配置逻辑设备个数
#define FF_VOLUMES 1
3、配置格式化文件系统接口
#define FF_USE_MKFS 1
该字段配置为1,可以使用f_mkfs接口来格式化空白flash(在本例中为必配参数)
六、diskio.c代码适配
note:留意下各个接口返回值,小心返回错误。
1、disk_status接口
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat = RES_OK;
switch (pdrv) {
case DEV_RAM :
stat = STA_NOINIT;
stat &= ~STA_NOINIT;
// translate the reslut code here
return stat;
}
return STA_NOINIT;
}
2、disk_initialize接口
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat = RES_OK;
int result;
switch (pdrv) {
case DEV_RAM :
// translate the reslut code here
return stat;
}
return STA_NOINIT;
}
3、disk_read接口
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_OK;
int ret;
switch (pdrv) {
case DEV_RAM :
// translate the arguments here
ret = b_flash_read_sector(sector, count, buff, 0XFF);
if(ret != 0)
{
res = RES_ERROR;
}
return res;
}
return RES_PARERR;
}
b_flash_read_sector,为底层flash读接口,下文会给出。
4、disk_write接口
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_OK;
int ret;
switch (pdrv) {
case DEV_RAM :
// translate the arguments here
ret = b_flash_write_sector(sector, count, (u8*)buff, 0XFF);
if(ret != 0)
{
res = RES_ERROR;
}
// translate the reslut code here
return res;
}
return RES_PARERR;
}
b_flash_write_sector,为底层flash写接口,下文会给出。
5、disk_ioctl接口
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res = RES_OK;
//LOG_INFO("disk_ioctl dev:%d\r\n", pdrv);
switch (pdrv) {
case DEV_RAM :
// Process of the command for the RAM drive
switch(cmd)
{
case CTRL_SYNC :
res = RES_OK;
break;
case CTRL_TRIM:
res = RES_OK;
break;
case GET_SECTOR_COUNT:
{
*(int*)buff = b_flash_get_sector_number();
}break;
case GET_SECTOR_SIZE:
{
*(int*)buff = b_flash_get_sector_size();
}break;
case GET_BLOCK_SIZE:
{
*(int*)buff = 1;
}break;
}
return res;
}
return RES_PARERR;
}
b_flash_get_sector_number,为底层flash获取扇区个数接口,下文会给出。
b_flash_get_sector_size,为底层flash获取扇区大小接口,下文会给出。
七、flash驱动实现
note:s32\u32\u16\u8等为自定义实现,如报错,请按需修改。
//文件系统管理的flash大小, 只能设置为块大小的整数倍
#define ENTER_FLASH_SIZE_FOR_FS (400)
//1KB的字节数
#define ENTER_FLASH_KB_SIZE (1024)
//文件系统管理的flash起始地址
#define ENTER_FLASH_FS_START_ADDR (FLASH_BANK1_END - (ENTER_FLASH_SIZE_FOR_FS * ENTER_FLASH_KB_SIZE) + 1)
//文件系统管理的flash结束
#define ENTER_FLASH_FS_STOP_ADDR (FLASH_BANK1_END)
//flash的块大小
#define ENTER_FLASH_BLOCK_SIZE (ENTER_FLASH_KB_SIZE * 2)
//flash的扇区大小
#define ENTER_FLASH_SECTOR_SIZE (ENTER_FLASH_BLOCK_SIZE * 1)
#define FLASH_ADDR_SECTOR(n) (ENTER_FLASH_FS_START_ADDR + ((u32)n * ENTER_FLASH_SECTOR_SIZE))
s32 b_flash_read_sector(u32 sector, u16 number, u8 *data, u32 timeout)
{
u32 addr = FLASH_ADDR_SECTOR(sector);
u32 i = 0;
u32 block_num = number * (ENTER_FLASH_SECTOR_SIZE / ENTER_FLASH_BLOCK_SIZE);
for(i = 0; i < block_num; i++)
{
u32 offset = (i * ENTER_FLASH_BLOCK_SIZE);
memcpy((void*)(data + offset), (void*)(addr + offset), ENTER_FLASH_BLOCK_SIZE);
}
return HAL_OK;
}
s32 b_flash_write_sector(u32 sector, u16 number, u8 *data, u32 timeout)
{
#define WRITE_FLASH_UNIT_BYTE_NUM (4)
s32 data_len = 0;
u32 error = 0;
u32 ret = HAL_OK;
FLASH_EraseInitTypeDef pEraseInit = {0};
u32 block_num = number * (ENTER_FLASH_SECTOR_SIZE / ENTER_FLASH_BLOCK_SIZE);
u32 addr = FLASH_ADDR_SECTOR(sector);
pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
pEraseInit.PageAddress = addr;
pEraseInit.NbPages = block_num;
data_len = block_num * ENTER_FLASH_BLOCK_SIZE;
HAL_FLASH_Unlock();
ret = HAL_FLASHEx_Erase(&pEraseInit, &error);
if(ret != HAL_OK)
{
HAL_FLASH_Lock();
return HAL_ERROR;
}
while(data_len > 0)
{
u32 *p_data32 = (u32*)data;
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, *p_data32);
addr += WRITE_FLASH_UNIT_BYTE_NUM;
data += WRITE_FLASH_UNIT_BYTE_NUM;
data_len -= WRITE_FLASH_UNIT_BYTE_NUM;
}
HAL_FLASH_Lock();
return HAL_OK;
}
u32 b_flash_get_sector_size(void)
{
return ENTER_FLASH_SECTOR_SIZE;
}
u32 b_flash_get_block_size(void)
{
return ENTER_FLASH_BLOCK_SIZE;
}
u32 b_flash_get_sector_number(void)
{
return ((ENTER_FLASH_FS_STOP_ADDR - ENTER_FLASH_FS_START_ADDR) / ENTER_FLASH_SECTOR_SIZE);
}
八、应用demo
note: 空白flash挂载文件系统会失败,根据挂载接口的返回值去格式化文件系统,格式化后,用户就可以进行文件系统操作了
1、挂载设备
#define DISK_FLASH "0:"
static int phase1_flash_fs_init(void)
{
static FATFS flash_fs;
u32 ret = 0;
MKFS_PARM opt = {0};
opt.fmt = FM_FAT;
static u8 work_buf[2048] = {0};
ret = f_mount(&flash_fs, DISK_FLASH, 1);
if(ret != FR_OK)
{
switch(ret)
{
case FR_NO_FILESYSTEM:
{
ret = f_mkfs(DISK_FLASH, &opt, work_buf, sizeof(work_buf));
}break;
case FR_MKFS_ABORTED:
{
ret = f_mkfs(DISK_FLASH, &opt, work_buf, sizeof(work_buf));
}break;
}
}
if(ret != 0)
{
LOG_ERROR("flash fs f_mkfs fail(%d)\r\n", ret);
}
else
{
LOG_INFO("flash fs f_mkfs succ\r\n");
}
return ret;
}
2、文件操作
#define USER_FILE_NAME "0:/data.txt"
s32 b_demo_user_fs(void)
{
FIL fp;
u32 real_write_num = 0;
FRESULT ret = f_open(&fp, USER_FILE_NAME, FA_CREATE_ALWAYS | FA_WRITE | FA_READ);
if(ret != FR_OK)
{
LOG_ERROR("f_open fail:%s\r\n", USER_FILE_NAME);
return -1;
}
u8 *test_w_b = "fs test write data";
ret = f_write(&fp, test_w_b, strlen(test_w_b), &real_write_num);
if(ret != FR_OK)
{
LOG_ERROR("f_open fail:%d\r\n", ret );
return -1;
}
LOG_INFO("write len:%d\r\n", real_write_num);
LOG_INFO("write data:%s\r\n", test_w_b);
ret = f_close(&fp);
if(ret != FR_OK)
{
LOG_ERROR("f_close fail:%d\r\n", ret);
return -1;
}
u32 real_read_num = 0;
u8 test_r_b[100] = {0};
ret = f_open(&fp, USER_FILE_NAME, FA_READ);
if(ret != FR_OK)
{
LOG_ERROR("f_open fail:%s\r\n", USER_FILE_NAME);
return -1;
}
ret = f_read(&fp, test_r_b, 100, &real_read_num);
if(ret != FR_OK)
{
LOG_ERROR("f_read fail:%d\r\n", ret);
return -1;
}
LOG_INFO("read len:%d\r\n", real_read_num);
LOG_INFO("read data:%s\r\n", test_r_b);
ret = f_close(&fp);
if(ret != FR_OK)
{
LOG_ERROR("f_close fail:%d\r\n", ret);
return -1;
}
return 0;
}