【学习笔记】XR872 GUI Littlevgl 8.0 移植(文件系统)

本文详细分析了LVGL的文件系统接口,指出了其在目录遍历和缓冲区管理上的不足,并提供了移植到不同平台的实现方法。LVGL通过抽象的驱动程序接口实现了文件操作的统一,允许在不同文件系统间轻松切换。作者分享了基于XR872和xradio-skylark-sdkv1.1.1,利用fatfs实现的移植代码,包括错误接口的修正和关键函数的实现。
摘要由CSDN通过智能技术生成

不得不提

 在移植的过程中,发现 LVGL 的文件操作接口并不十分完善,在我看来, LVGL 的文件操作接口,应该更多的是为了 LVGL 内部接口方便读取资源文件而设立的,例如读取图像文件,加载字库文件等等。

遍历目录也形同鸡肋,特别是读取目录的接口定义,简直要吐血,最后一个参数居然没有附带缓冲区长度,在使用的过程中,程序员如果对缓冲区长度未把握好,极容易出现内存溢出的问题。

例如缓冲区定义为 64 个字节,如果在调用 lv_fs_dir_read() 接口读取目录时,目录路径深度超过 64 字节,就会出现缓冲区溢出的问题,这很有可能在研发前期没有复现,而在用户使用的时候,才会出现。这对一个产品来说,是一个非常严重的问题。

/**
 * Read the next filename form a directory.
 * The name of the directories will begin with '/'
 * @param rddir_p pointer to an initialized 'fs_dir_t' variable
 * @param fn pointer to a buffer to store the filename
 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn);

对应的实现:

lv_fs_res_t (*dir_read_cb)(struct _lv_fs_drv_t * drv, void * rddir_p, char * fn);

可以看到并没有字段可以指出,承担结果返回的 fn 缓冲区有多大。

接口分析

LGVL 对文件操作的接口进行统一重新定义,通过结构体 lv_fs_drv_t 对这些操作接口进行管理,LVGL 内部涉及到文件操作相关的接口,均调用封装过的接口,这样一来就得以脱离具体的文件系统接口,便于移植到不同的平台上使用。

一个 lv_fs_drv_t 表示为一种文件系统驱动程序,LVGL 通过链表来保存多个文件系统驱动程序,当调用打开文件或者目录时,会根据路径的第一个字符,遍历该链表,匹配指定的文件系统驱动程序。

如下图所示,可以实现三种存储类型不同的文件操作,SD卡文件读写实现(S),Flash 文件读写实现(F),Samba 网络文件读写实现(N),并通过 lv_fs_drv_register() 注册到 LVGL 系统中,LVGL 会以链表的方式组织这三种操作接口。

当时需要打开某些文件的时候,如果该文件存储在 SD 卡中,那么路径以 S:/ 开头即可,如果该文件存储在 Flash 中,那么路径以 F:/ 开头,同理,Samba 则以 N:/ 开头,LVGL 会通过路径的第一个字符,来遍历整个链表,只有匹配成功,才会调用相应的文件操作接口。

网上有看到通过区分 letter 的方式来 switch 不同的操作方法,这就意味着不同的文件读写实现会耦合到一个源文件里面,其实大可不必,LVGL 已经考虑到很周全了,按照 LVGL 的方式,完全可以实现不同的文件读写实现分属不同的源文件中,彼此互不影响,新增新的文件读写实现,增加新的源文件即可,不需要修改原来的源文件。(是不是很像设计模式中的开闭原则)

 移植方法

通过上面分析,只需要按照要求实现 lv_fs_drv_t 中定义的回调函数,然后通过 lv_fs_drv_register() 注册到链表中即可。

LVGL 8.0 版本的 lv_port_fs_template.c 模板文件的接口定义与 lv_fs_drv_t 的定义并不完全匹配,不能直接使用。

这是原来错误的接口定义:

static void fs_init(void);
static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos);
static lv_fs_res_t fs_size (lv_fs_drv_t * drv, void * file_p, uint32_t * size_p);
static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static lv_fs_res_t fs_remove (lv_fs_drv_t * drv, const char *path);
static lv_fs_res_t fs_trunc (lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_rename (lv_fs_drv_t * drv, const char * oldname, const char * newname);
static lv_fs_res_t fs_free (lv_fs_drv_t * drv, uint32_t * total_p, uint32_t * free_p);
static lv_fs_res_t fs_dir_open (lv_fs_drv_t * drv, void * rddir_p, const char *path);
static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * rddir_p, char *fn);
static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * rddir_p);

这是正确的接口定义:

static void fs_init(fs_private_t *fs_private);
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);

只要按照要求实现 lv_fs_drv_t 中定义的回调函数,并注册就行,所以实际上不需要参考  lv_port_fs_template.c 写,自己实现就行。

以下是基于 XR872 ,xradio-skylark-sdk v1.1.1 ,fatfs 实现的移植成功的代码:

/**
 * @file lv_port_fs_templ.c
 *
 */

 /*Copy this file as "lv_port_fs.c" and set this value to "1" to enable content*/
#if 1

/*********************
 *      INCLUDES
 *********************/
#include "fs/fatfs/ff.h"
#include "common/framework/fs_ctrl.h"
#include "lv_port_fs.h"

#include <stdio.h>
#define iprintf(fmt, arg...)	printf("[lv_port_fs] "fmt, ##arg)
/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

typedef struct{
	bool is_ready;
}fs_private_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void fs_init(fs_private_t *fs_private);
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
 **********************/

/**********************
 * GLOBAL PROTOTYPES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_port_fs_init(void)
{
    /*----------------------------------------------------
     * Initialize your storage device and File System
     * -------------------------------------------------*/
	static fs_private_t fs_private;
	lv_memset(&fs_private, 0x00, sizeof(fs_private_t));
    fs_init(&fs_private);

    /*---------------------------------------------------
     * 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.user_data = (void *)&fs_private;
    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(fs_private_t *fs_private)
{
    /*E.g. for FatFS initialize the SD card and FatFS itself*/
	if (fs_ctrl_mount(FS_MNT_DEV_TYPE_SDCARD, 0) != 0) {
		fs_private->is_ready = false;
		iprintf("fs_ctrl_mount failed...\n");
		return ;
	}
	iprintf("fs_ctrl_mount success...\n");
	fs_private->is_ready = true;
	return ;
}

/***********************************************************************************
*	函数: 			fs_ready
*	功能:			LVGL 会通过此函数判断文件系统是否就绪
*	参数:						
*		drv:		上下文, 我的理解, 便于取 letter、user_data 成员变量
*	返回值:			
*		true:		已就绪   
*		false:		未就绪
***********************************************************************************/	
static bool fs_ready(struct _lv_fs_drv_t * drv)
{
	fs_private_t *fs_private = (fs_private_t *)drv->user_data;
	if(false == fs_private->is_ready){
		fs_private->is_ready = fs_ctrl_mount(FS_MNT_DEV_TYPE_SDCARD, 0) != 0 ? false : true;
		if(false == fs_private->is_ready){
			iprintf("fs_ctrl_mount failed...\n");
		}
	}
	return fs_private->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;
	
	FIL *fil = (FIL *)lv_mem_alloc(sizeof(FIL));
	if(NULL == fil){
		iprintf("fs_open: lv_mem_alloc failed %d Byte...\n", sizeof(FIL));
		return NULL;
	}
	
	if(mode & LV_FS_MODE_WR){
		f_mode |= FA_WRITE | FA_CREATE_ALWAYS;
	}
	
	if(mode & LV_FS_MODE_RD){
		f_mode |= FA_READ;
	}

    if ( (fr_res = f_open(fil, path, f_mode)) != FR_OK) {
		lv_mem_free(fil);
		iprintf("f_open failed:[0x%.2X][%d][%s]...\n", f_mode, fr_res, path);
		return NULL;
	}
	
	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) {
		iprintf("f_read failed:[%d]...\n", fr_res);
	}
	else{
		iprintf("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){
		iprintf("fs_write failed disk full: %d < %d...\n", *bw, btw);
		return LV_FS_RES_FULL;
	}
	
	if(FR_OK != fr_res){
		iprintf("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){
		iprintf("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);
		iprintf("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){
		iprintf("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

测试代码

#include "common/framework/platform_init.h"
#include <string.h>
#include <stdio.h>
#include "prj_config.h"
#include "kernel/os/os.h"
#include "lvgl/lvgl.h"
#include "lvgl_driver/lv_port_disp.h"
#include "lvgl_driver/lv_port_fs.h"

void lv_example_fs_file(void)
{
	#define TEST_FILE_PATH	"S:/test.txt"
	#define TEST_TEXT		"lvgl fs write read test..."
	
	lv_fs_file_t file;
	lv_fs_res_t	 fsres = LV_FS_RES_OK;
	
    fsres = lv_fs_open(&file, TEST_FILE_PATH, LV_FS_MODE_RD | LV_FS_MODE_WR);
    if(fsres != LV_FS_RES_OK){
        printf("lv_fs_open failed:[%d][%s]\n", fsres, TEST_FILE_PATH);
		return ;
	}
	printf("lv_fs_open success:[%s]\n", TEST_FILE_PATH);
	
	uint32_t wbyte = 0x00, rbyte = 0x00;
	fsres = lv_fs_write(&file, TEST_TEXT, strlen(TEST_TEXT), &wbyte);
	if(fsres != LV_FS_RES_OK){
        printf("lv_fs_write failed:[%d][%s]\n", fsres, TEST_FILE_PATH);
		lv_fs_close(&file);
		return ;
	}
	printf("lv_fs_write success:[%s]\n", TEST_FILE_PATH);
	lv_fs_seek(&file, 0x00, LV_FS_SEEK_SET);
	
	char buff[100] = {0};
	fsres = lv_fs_read(&file, buff, sizeof(buff), &rbyte);
	if(fsres != LV_FS_RES_OK){
        printf("lv_fs_read failed:[%d][%s]\n", fsres, TEST_FILE_PATH);
		lv_fs_close(&file);
		return ;
	}
	printf("lv_fs_read success:[%s]\n", TEST_FILE_PATH);
	lv_fs_close(&file);
	printf("lv_fs_close success:[%s]\n", TEST_FILE_PATH);
	
	printf("write %d byte, read %d byte\n", wbyte, rbyte);
	printf("read buff:[%s]\n", buff);
	
	return ;
}

void lv_example_fs_dir(void)
{
	#define TEST_DIR_PATH	"S:/"
	lv_fs_dir_t rddir;
	lv_fs_res_t	 fsres = LV_FS_RES_OK;
	
	fsres = lv_fs_dir_open(&rddir, TEST_DIR_PATH);
	if(fsres != LV_FS_RES_OK){
		printf("lv_fs_dir_open failed:[%d][%s]\n", fsres, TEST_DIR_PATH);
		return ;
	}
	printf("lv_fs_dir_open success:[%s]\n", TEST_DIR_PATH);
	
	char buff[257] = {0};
	while( ( fsres = lv_fs_dir_read(&rddir, buff) ) == LV_FS_RES_OK){
		if('\0' == buff[0]){
			printf("read dir done\n");
			lv_fs_dir_close(&rddir);
			printf("lv_fs_dir_close success:[%s]\n", TEST_DIR_PATH);
			return ;
		}
		printf("[%s]\n", buff);	
	}
	printf("lv_fs_dir_read failed:[%d][%s]\n", fsres, TEST_DIR_PATH);
	
	return ;
}

int main(void)
{
	platform_init();
	
	lv_init(); 
	lv_port_fs_init();
	lv_tick_handle_init();

	lv_example_fs_file();
	lv_example_fs_dir();

	while(1){
		OS_Sleep(3);
	}
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值