FatFs操作SD卡上的文件(SWM341芯片作为案例)

软件开发大郭
0 评论
/
36 阅读
/
25622 字
22 2022-05

简单介绍FATFS

FatFS一般作为一个文件系统,存储介质支持USB,SD卡,NANDFlash等等, 这里主要介绍下如何对接SD卡使用。

实现的功能和效果

由于UI是采用LVGL来实现的,而且大面积用到了贴图和动画效果,占用的存储空间远远超过芯片的存储空间, 所以准备把资源部分单独以BIN格式的文件存放到sd卡上,sd卡的文件系统格式采用FAT32的,这样子程序和资源 就分离开了,程序直接存放到芯片的flash中,图片资源独立存放到sd卡上。

先看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
    暂无数据