简单介绍FATFS
FatFS一般作为一个文件系统,存储介质支持USB,SD卡,NANDFlash等等,
这里主要介绍下如何对接SD卡使用。
实现的功能和效果
由于UI是采用LVGL来实现的,而且大面积用到了贴图和动画效果,占用的存储空间远远超过芯片的存储空间,
所以准备把资源部分单独以BIN格式的文件存放到sd卡上,sd卡的文件系统格式采用FAT32的,这样子程序和资源
就分离开了,程序直接存放到芯片的flash中,图片资源独立存放到sd卡上。
先看SD卡的硬件原理图
SD卡原理图
主控MCU
对接底层IO
操作SD卡,所有的操作模块都对应的在DEV_MMC分支里操作。
包含进IO操作相关的头文件
#include "SWM341.h"
我这里是SWM341的芯片,如果是其他芯片例如STM32的,则要包含对应的IO操作的头文件进来。
IO初始化
对应位置:fatfs/diskio.c
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize(BYTE pdrv /* Physical drive nmuber to identify the drive */
) {
DSTATUS stat;
int result;
switch (pdrv) {
case DEV_RAM:
//result = RAM_disk_initialize();
// translate the reslut code here
return stat;
case DEV_MMC:
//result = MMC_disk_initialize();
// translate the reslut code here
PORT_Init(PORTM, PIN2, PORTM_PIN2_SD_CLK, 0);
PORT_Init(PORTM, PIN4, PORTM_PIN4_SD_CMD, 1);
PORT_Init(PORTM, PIN5, PORTM_PIN5_SD_D0, 1);
PORT_Init(PORTM, PIN6, PORTM_PIN6_SD_D1, 1);
PORT_Init(PORTN, PIN0, PORTN_PIN0_SD_D2, 1);
PORT_Init(PORTN, PIN1, PORTN_PIN1_SD_D3, 1);
result = SDIO_Init(10000000);
if (result == SD_RES_OK) {
stat = RES_OK;
sd_initialized = 1;
} else {
stat = STA_NOINIT;
sd_initialized = 0;
}
return stat;
return stat;
case DEV_USB:
//result = USB_disk_initialize();
// translate the reslut code here
return stat;
}
return STA_NOINIT;
}
读取IO配置
/*-----------------------------------------------------------------------*/
/* 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;
int result;
switch (pdrv) {
case DEV_RAM:
// translate the arguments here
//result = RAM_disk_read(buff, sector, count);
// translate the reslut code here
res = RES_PARERR;
return res;
case DEV_MMC:
// translate the arguments here
//result = MMC_disk_read(buff, sector, count);
// translate the reslut code here
if (count == 1) {
result = SDIO_BlockRead(sector, (uint32_t*) buff);
} else {
result = SDIO_MultiBlockRead(sector, count, (uint32_t*) buff);
}
if (result == SD_RES_OK)
res = RES_OK;
else
res = RES_ERROR;
return res;
case DEV_USB:
// translate the arguments here
//result = USB_disk_read(buff, sector, count);
// translate the reslut code here
res = RES_PARERR;
return res;
}
return RES_PARERR;
}
写IO配置
/*-----------------------------------------------------------------------*/
/* 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;
int result;
switch (pdrv) {
case DEV_RAM:
// translate the arguments here
//result = RAM_disk_write(buff, sector, count);
// translate the reslut code here
return res;
case DEV_MMC:
// translate the arguments here
//result = MMC_disk_write(buff, sector, count);
// translate the reslut code here
if (count == 1) {
result = SDIO_BlockWrite(sector, (uint32_t*) buff);
} else {
result = SDIO_MultiBlockWrite(sector, count, (uint32_t*) buff);
}
if (result == SD_RES_OK)
res = RES_OK;
else
res = RES_ERROR;
return res;
case DEV_USB:
// translate the arguments here
//result = USB_disk_write(buff, sector, count);
// translate the reslut code here
return res;
}
return RES_PARERR;
}
#endif
IO控制
/*-----------------------------------------------------------------------*/
/* 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;
int result;
switch (pdrv) {
case DEV_RAM:
// Process of the command for the RAM drive
res = RES_PARERR;
case DEV_MMC:
// Process of the command for the MMC/SD card
res = RES_OK;
case DEV_USB:
// Process of the command the USB drive
res = RES_PARERR;
}
return RES_PARERR;
}
结合LVGL使用
完整的fatfs/diskio.c 文件
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be */
/* attached to the FatFs via a glue function rather than modifying it. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include "SWM341.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 */
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
static int sd_initialized = 0;
DSTATUS disk_status(BYTE pdrv /* Physical drive nmuber to identify the drive */
) {
DSTATUS stat;
int result;
switch (pdrv) {
case DEV_RAM:
//result = RAM_disk_status();
// translate the reslut code here
stat = STA_NOINIT;
return stat;
case DEV_MMC:
//result = MMC_disk_status();
// translate the reslut code here
if (sd_initialized)
stat = RES_OK;
else
stat = STA_NOINIT;
return stat;
return stat;
case DEV_USB:
//result = USB_disk_status();
stat = STA_NOINIT;
// translate the reslut code here
return stat;
}
return STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize(BYTE pdrv /* Physical drive nmuber to identify the drive */
) {
DSTATUS stat;
int result;
switch (pdrv) {
case DEV_RAM:
//result = RAM_disk_initialize();
// translate the reslut code here
return stat;
case DEV_MMC:
//result = MMC_disk_initialize();
// translate the reslut code here
PORT_Init(PORTM, PIN2, PORTM_PIN2_SD_CLK, 0);
PORT_Init(PORTM, PIN4, PORTM_PIN4_SD_CMD, 1);
PORT_Init(PORTM, PIN5, PORTM_PIN5_SD_D0, 1);
PORT_Init(PORTM, PIN6, PORTM_PIN6_SD_D1, 1);
PORT_Init(PORTN, PIN0, PORTN_PIN0_SD_D2, 1);
PORT_Init(PORTN, PIN1, PORTN_PIN1_SD_D3, 1);
result = SDIO_Init(10000000);
if (result == SD_RES_OK) {
stat = RES_OK;
sd_initialized = 1;
} else {
stat = STA_NOINIT;
sd_initialized = 0;
}
return stat;
case DEV_USB:
//result = USB_disk_initialize();
// translate the reslut code here
return stat;
}
return STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* 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;
int result;
switch (pdrv) {
case DEV_RAM:
// translate the arguments here
//result = RAM_disk_read(buff, sector, count);
// translate the reslut code here
res = RES_PARERR;
return res;
case DEV_MMC:
// translate the arguments here
//result = MMC_disk_read(buff, sector, count);
// translate the reslut code here
if (count == 1) {
result = SDIO_BlockRead(sector, (uint32_t*) buff);
} else {
result = SDIO_MultiBlockRead(sector, count, (uint32_t*) buff);
}
if (result == SD_RES_OK)
res = RES_OK;
else
res = RES_ERROR;
return res;
case DEV_USB:
// translate the arguments here
//result = USB_disk_read(buff, sector, count);
// translate the reslut code here
res = RES_PARERR;
return res;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------*/
/* 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;
int result;
switch (pdrv) {
case DEV_RAM:
// translate the arguments here
//result = RAM_disk_write(buff, sector, count);
// translate the reslut code here
return res;
case DEV_MMC:
// translate the arguments here
//result = MMC_disk_write(buff, sector, count);
// translate the reslut code here
if (count == 1) {
result = SDIO_BlockWrite(sector, (uint32_t*) buff);
} else {
result = SDIO_MultiBlockWrite(sector, count, (uint32_t*) buff);
}
if (result == SD_RES_OK)
res = RES_OK;
else
res = RES_ERROR;
return res;
case DEV_USB:
// translate the arguments here
//result = USB_disk_write(buff, sector, count);
// translate the reslut code here
return res;
}
return RES_PARERR;
}
#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;
int result;
switch (pdrv) {
case DEV_RAM:
// Process of the command for the RAM drive
res = RES_PARERR;
case DEV_MMC:
// Process of the command for the MMC/SD card
res = RES_OK;
case DEV_USB:
// Process of the command the USB drive
res = RES_PARERR;
}
return RES_PARERR;
}
修改port/lv_port_fs.c
文件
/**
* @file lv_port_fs.c
*
*/
/*Copy this file as "lv_port_fs.c" and set this value to "1" to enable content*/
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_fs.h"
#include "fatfs/ff.h"
#include "../lvgl/lvgl.h"
#include "stdio.h"
#include <stdio.h>
#define log(fmt, arg...) printf("[lv_port_fs] "fmt, ##arg)
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void fs_init(void);
static bool fs_ready(struct _lv_fs_drv_t *drv);
static void* fs_open(struct _lv_fs_drv_t *drv, const char *path,
lv_fs_mode_t mode);
static lv_fs_res_t fs_close(struct _lv_fs_drv_t *drv, void *file_p);
static lv_fs_res_t fs_read(struct _lv_fs_drv_t *drv, void *file_p, void *buf,
uint32_t btr, uint32_t *br);
static lv_fs_res_t fs_write(struct _lv_fs_drv_t *drv, void *file_p,
const void *buf, uint32_t btw, uint32_t *bw);
static lv_fs_res_t fs_seek(struct _lv_fs_drv_t *drv, void *file_p, uint32_t pos,
lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(struct _lv_fs_drv_t *drv, void *file_p,
uint32_t *pos_p);
static void* fs_dir_open(struct _lv_fs_drv_t *drv, const char *path);
static lv_fs_res_t fs_dir_read(struct _lv_fs_drv_t *drv, void *rddir_p,
char *fn);
static lv_fs_res_t fs_dir_close(struct _lv_fs_drv_t *drv, void *rddir_p);
/**********************
* STATIC VARIABLES
**********************/
static FATFS fatfs;
static bool fs_is_ready;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_port_fs_init(void) {
/*----------------------------------------------------
* Initialize your storage device and File System
* -------------------------------------------------*/
fs_init();
/*---------------------------------------------------
* Register the file system interface in LVGL
*--------------------------------------------------*/
/*Add a simple drive to open images*/
static lv_fs_drv_t fs_drv;
lv_fs_drv_init(&fs_drv);
/*Set up fields...*/
fs_drv.letter = 'S';
fs_drv.ready_cb = fs_ready;
fs_drv.open_cb = fs_open;
fs_drv.close_cb = fs_close;
fs_drv.read_cb = fs_read;
fs_drv.write_cb = fs_write;
fs_drv.seek_cb = fs_seek;
fs_drv.tell_cb = fs_tell;
fs_drv.dir_open_cb = fs_dir_open;
fs_drv.dir_read_cb = fs_dir_read;
fs_drv.dir_close_cb = fs_dir_close;
lv_fs_drv_register(&fs_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
//LV_FS_RES_OK=0,
//LV_FS_RES_HW_ERR, /*低级硬件错误*/
//LV_FS_RES_FS_ERR, /*文件系统结构中的错误*/
//LV_FS_RES_NOT_EX, /*驱动程序、文件或目录不存在*/
//LV_FS_RES_FULL, /*磁盘已满*/
//LV_FS_RES_LOCKED, /*文件已打开*/
//LV_FS_RES_DENIED, /*访问被拒绝。检查“fs_打开”模式和写保护*/
//LV_FS_RES_BUSY, /*文件系统现在无法处理它,请稍后再试*/
//LV_FS_RES_TOUT, /*过程时间路由*/
//LV_FS_RES_NOT_IMP, /*请求的函数未实现*/
//LV_FS_RES_OUT_OF_MEM, /*内存不足,无法执行内部操作*/
//LV_FS_RES_INV_PARAM, /*参数中的参数无效*/
//LV_FS_RES_UNKNOWN, /*其他未知错误*/
static lv_fs_res_t to_lvfs_res(FRESULT res) {
switch (res) {
case FR_OK:
return LV_FS_RES_OK; /*(0)成功*/
case FR_DISK_ERR:
return LV_FS_RES_HW_ERR; /*(1)在低级别磁盘I/O层中发生了一个硬错误*/
case FR_INT_ERR:
return LV_FS_RES_FS_ERR; /*(2)断言失败*/
case FR_NOT_READY:
return LV_FS_RES_HW_ERR; /*(3)物理驱动器无法工作*/
case FR_NO_FILE:
return LV_FS_RES_NOT_EX; /*(4)找不到该文件*/
case FR_NO_PATH:
return LV_FS_RES_NOT_EX; /*(5)找不到路径*/
case FR_INVALID_NAME:
return LV_FS_RES_INV_PARAM; /*(6)路径名格式无效*/
case FR_DENIED:
return LV_FS_RES_DENIED; /*(7)拒绝访问:磁盘以满\使用写模式打开只读文件\删除只读文件...等等*/
case FR_EXIST:
return LV_FS_RES_DENIED; /*(8)已经存在同名的文件或目录*/
case FR_INVALID_OBJECT:
return LV_FS_RES_INV_PARAM; /*(9)文件/目录对象无效*/
case FR_WRITE_PROTECTED:
return LV_FS_RES_DENIED; /*(10)物理驱动器是写保护的*/
case FR_INVALID_DRIVE:
return LV_FS_RES_INV_PARAM; /*(11)逻辑驱动器号无效*/
case FR_NOT_ENABLED:
return LV_FS_RES_HW_ERR; /*(12)当前卷没有工作区*/
case FR_NO_FILESYSTEM:
return LV_FS_RES_HW_ERR; /*(13)没有有效的FAT卷*/
case FR_MKFS_ABORTED:
return LV_FS_RES_INV_PARAM; /*(14)f_MKFS()在格式化开始前终止*/
case FR_TIMEOUT:
return LV_FS_RES_BUSY; /*(15)无法在定义的时间段内获得访问卷的授权*/
case FR_LOCKED:
return LV_FS_RES_LOCKED; /*(16)根据文件共享策略拒绝该操作*/
case FR_NOT_ENOUGH_CORE:
return LV_FS_RES_OUT_OF_MEM; /*(17)无法分配LFN工作缓冲区*/
case FR_TOO_MANY_OPEN_FILES:
return LV_FS_RES_BUSY; /*(18)打开的文件数目大于 FF_FS_LOCK*/
case FR_INVALID_PARAMETER:
return LV_FS_RES_INV_PARAM; /*(19)给定参数无效*/
}
return LV_FS_RES_UNKNOWN;
}
/*Initialize your Storage device and File system.*/
static void fs_init(void) {
/*E.g. for FatFS initialize the SD card and FatFS itself*/
FRESULT res;
res = f_mount(&fatfs, "sd:", 1);
if (res != FR_OK) {
fs_is_ready = false;
log("f_mount failed...\n");
return;
}
log("f_mount success...\n");
fs_is_ready = true;
return;
}
/***********************************************************************************
* 函数: fs_ready
* 功能: LVGL 会通过此函数判断文件系统是否就绪
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* 返回值:
* true: 已就绪
* false: 未就绪
***********************************************************************************/
static bool fs_ready(struct _lv_fs_drv_t *drv) {
FRESULT res;
if (false == fs_is_ready) {
res = f_mount(&fatfs, "sd:", 1);
if (res != FR_OK) {
fs_is_ready = false;
log("f_mount failed...\n");
return fs_is_ready;
}
if (false == fs_is_ready) {
log("f_mount failed...\n");
}
}
return fs_is_ready;
}
/***********************************************************************************
* 函数: fs_open
* 功能: LVGL 会通过此函数打开指定的文件
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* path: 要打开的文件路径
* mode: 打开模式 LV_FS_MODE_WR\LV_FS_MODE_RD
* 返回值:
* >0: 返回打开文件的句柄,后续操作皆以此句柄为核心(void * file_p)
* NULL: 打开文件失败
***********************************************************************************/
static void* fs_open(struct _lv_fs_drv_t *drv, const char *path,
lv_fs_mode_t mode) {
FRESULT fr_res;
unsigned char f_mode = 0x00;
char *path_buf = NULL;
uint16_t path_len = strlen(path);
/************************************************
* 在这里,我们需要先进行设备的判断,因为我们实际上
* 可能不只有一个设备,而且LVGL里使用letter(即单个字符)
* 作为驱动号
* 而在FatFS里使用VOLUME_STR(字符串)或者一个字节的pdrv
* 驱动号,使用前要进行转换。
*
* 有一种方便的方法,就是上面提到的user_data,我们可以把
* VOLUME_STR提前装入user_data,那我们的程序进行判断时
* 耦合度会更低,在更多设备时,这种方法会更方便
*************************************************/
// 根据传入的letter判断是什么存储设备
switch (drv->letter) {
case 'S': // SD card
path_buf = (char*) lv_mem_alloc(sizeof(char) * (path_len + 4));
log("origin path:%s",path);
sprintf(path_buf, "sd:/%s", path);
break;
case 'F': // SPI FALSH
path_buf = (char*) lv_mem_alloc(sizeof(char) * (path_len + 6));
sprintf(path_buf, "SPIF:/%s", path);
break;
default:
printf("No drive %c\n", drv->letter);
return LV_FS_RES_NOT_EX;
}
/* 调用FatFs的函数 */
FIL *fil = (FIL*) lv_mem_alloc(sizeof(FIL));
if (NULL == fil) {
log("fs_open: lv_mem_alloc failed %d Byte...\n", sizeof(FIL));
return NULL;
}
/* 文件操作方法,将FatFS的转换成LVGL的操作方法 */
if (mode == LV_FS_MODE_WR) {
f_mode = FA_OPEN_ALWAYS | FA_WRITE;
} else if (mode == LV_FS_MODE_RD) {
f_mode = FA_OPEN_EXISTING | FA_READ;
} else if (mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) {
f_mode = FA_WRITE | FA_READ;
}
if ((fr_res = f_open(fil, path_buf, f_mode)) != FR_OK) {
//释放内存
lv_mem_free(fil);
log("f_open failed:[0x%.2X][%d][%s]...\n", f_mode, fr_res, path_buf);
return NULL;
}
//释放内存
lv_mem_free(path_buf);
return (void*) fil;
}
/***********************************************************************************
* 函数: fs_close
* 功能: LVGL 会通过此函数关闭指定的文件
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* file_p: 文件对应的句柄, 由 fs_open() 成功时取得
* 返回值: 参考 lv_fs_res_t
***********************************************************************************/
static lv_fs_res_t fs_close(struct _lv_fs_drv_t *drv, void *file_p) {
if (file_p) {
f_close((FIL*) file_p);
lv_mem_free(file_p);
return LV_FS_RES_OK;
}
return LV_FS_RES_INV_PARAM;
}
/***********************************************************************************
* 函数: fs_close
* 功能: LVGL 会通过此函数读取指定的文件
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* file_p: 文件对应的句柄, 由 fs_open() 成功时取得
* buf: 存储文件数据的缓冲区
* btr: 存储文件数据的缓冲区大小
* br: 实际读取文件的数据长度
* 返回值: 参考 lv_fs_res_t
***********************************************************************************/
static lv_fs_res_t fs_read(struct _lv_fs_drv_t *drv, void *file_p, void *buf,
uint32_t btr, uint32_t *br) {
FRESULT fr_res = FR_OK;
if ((fr_res = f_read((FIL*) file_p, buf, btr, br)) != FR_OK) {
log("f_read failed:[%d]...\n", fr_res);
} else {
log("f_read success:btr:[%d] br:[%d]...\n", btr, *br);
}
return to_lvfs_res(fr_res);
}
/***********************************************************************************
* 函数: fs_write
* 功能: LVGL 会通过此函数写入指定的文件
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* file_p: 文件对应的句柄, 由 fs_open() 成功时取得
* buf: 存储要写入文件的数据
* btw: 写于文件数据的长度
* bw: 实际写入文件的数据长度
* 返回值: 参考 lv_fs_res_t
***********************************************************************************/
static lv_fs_res_t fs_write(struct _lv_fs_drv_t *drv, void *file_p,
const void *buf, uint32_t btw, uint32_t *bw) {
FRESULT fr_res = f_write((FIL*) file_p, buf, btw, bw);
if (FR_OK == fr_res && *bw < btw) {
log("fs_write failed disk full: %d < %d...\n", *bw, btw);
return LV_FS_RES_FULL;
}
if (FR_OK != fr_res) {
log("f_write failed:[%d]...\n", fr_res);
}
return to_lvfs_res(fr_res);
}
/***********************************************************************************
* 函数: fs_seek
* 功能: LVGL 会通过此函数调整文件指针的偏移位置
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* file_p: 文件对应的句柄, 由 fs_open() 成功时取得
* pos: 文件位置
* whence: 指定以什么方式调整: LV_FS_SEEK_SET(起始)、 LV_FS_SEEK_CUR(当前)、 LV_FS_SEEK_END(结束)
* 返回值: 参考 lv_fs_res_t
***********************************************************************************/
static lv_fs_res_t fs_seek(struct _lv_fs_drv_t *drv, void *file_p, uint32_t pos,
lv_fs_whence_t whence) {
switch (whence) {
case LV_FS_SEEK_SET:
return to_lvfs_res(f_lseek((FIL*) file_p, pos));
case LV_FS_SEEK_CUR:
return to_lvfs_res(f_lseek((FIL*) file_p, f_tell((FIL *)file_p) + pos));
case LV_FS_SEEK_END:
return to_lvfs_res(f_lseek((FIL*) file_p, f_size((FIL *)file_p) + pos));
}
return LV_FS_RES_INV_PARAM;
}
/***********************************************************************************
* 函数: fs_tell
* 功能: LVGL 会通过此函数获取当前文件指针的位置
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* file_p: 文件对应的句柄, 由 fs_open() 成功时取得
* pos: 通过此参数返回文件指针的位置
* 返回值: 参考 lv_fs_res_t
***********************************************************************************/
static lv_fs_res_t fs_tell(struct _lv_fs_drv_t *drv, void *file_p,
uint32_t *pos_p) {
return to_lvfs_res(f_tell((FIL* )file_p));
}
/***********************************************************************************
* 函数: fs_dir_open
* 功能: LVGL 会通过此函数打开当前目录
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* path: 要打开的目录路径
* 返回值:
* >0: 返回打开目录的句柄,后续操作皆以此句柄为核心(void * rddir_p)
* NULL: 打开目录失败
***********************************************************************************/
static void* fs_dir_open(struct _lv_fs_drv_t *drv, const char *path) {
FRESULT fr_res = FR_OK;
DIR *dir = (DIR*) lv_mem_alloc(sizeof(DIR));
if (NULL == dir) {
log("f_opendir: lv_mem_alloc %d byte failed...\n", sizeof(DIR));
return NULL;
}
if ((fr_res = f_opendir(dir, path)) != FR_OK) {
lv_mem_free(dir);
log("f_opendir failed:[%d][%s]...\n", fr_res, path);
return NULL;
}
return (void*) dir;
}
/***********************************************************************************
* 函数: fs_dir_read
* 功能: LVGL 会通过此函数获取当前目录的条目(即该目录下有哪些文件夹和文件)
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* rddir_p: 目录对应的句柄, 由 fs_dir_open() 成功时取得
* fn: 通过此参数返回目录内容, 一次只返回一个条目(如有十个文件, 只返回一个)
* 返回值: 参考 lv_fs_res_t
* 备注说明:
* 1. 没有条目或读取完条目则返回空文本内容到 fn (即 \0)
* 2. LGVL 对上述似乎并没有明确要求, 并且参数也显得不合理(缺少缓冲区长度信息)
* 3. 使用时务必注意 fn 越界问题, fn 缓冲区大小必须大于 FILINFO->fname+1 大小
***********************************************************************************/
static lv_fs_res_t fs_dir_read(struct _lv_fs_drv_t *drv, void *rddir_p,
char *fn) {
FRESULT fr_res = FR_OK;
FILINFO fno;
fr_res = f_readdir((DIR*) rddir_p, &fno);
if (fr_res != FR_OK) {
log("f_readdir failed:[%d]...\n", fr_res);
return to_lvfs_res(fr_res);
}
if ('\0' == fno.fname[0]) {
fn[0] = '\0';
return LV_FS_RES_OK;
}
if (fno.fattrib & AM_DIR) {
fn[0] = '/';
fn++;
}
// 注意, 这里会存在内存溢出的风险, LVGL 的接口并未提供缓冲区长度信息
strcpy(fn, fno.fname); // strncpy(fn, fno.fname, fn_size);
return LV_FS_RES_OK;
}
/***********************************************************************************
* 函数: fs_dir_close
* 功能: LVGL 会通过此函数关闭指定目录
* 参数:
* drv: 上下文, 我的理解, 便于取 letter、user_data 成员变量
* rddir_p: 目录对应的句柄, 由 fs_dir_open() 成功时取得
* 返回值: 参考 lv_fs_res_t
***********************************************************************************/
static lv_fs_res_t fs_dir_close(struct _lv_fs_drv_t *drv, void *rddir_p) {
if (rddir_p) {
f_closedir((DIR*) rddir_p);
lv_mem_free(rddir_p);
return LV_FS_RES_OK;
}
return LV_FS_RES_INV_PARAM;
}
#else /*Enable this file at the top*/
/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif