【学习笔记】XR872 GUI Littlevgl 8.0 移植(文件系统)
不得不提
在移植的过程中,发现 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;
}

【学习笔记】XR872 GUI Littlevgl 8.0 移植(文件系统)的更多相关文章
- Cocos2D-X2.2.3学习笔记9(处理重力感应事件,移植到Android加入两次返回退出游戏效果)
		
这节我们来学习Cocos2d-x的最后一节.怎样处理重力感应事件.移植到Android后加入再按一次返回键退出游戏等.我这里用的Android.IOS不会也没设备呃 效果图不好弄,由于是要移植到真机上 ...
 - 【opencv学习笔记二】opencv3.4.0组件结构说明
		
在学习opencv使用之前我们先来看一下opencv有哪些组件结构.至于OpenCV组件结构的研究方法, 我们不妨管中窥豹,通过opencv安装路径下include目录里面头文件的分类存放,来一窥Op ...
 - 【opencv学习笔记四】opencv3.4.0图形用户接口highgui函数解析
		
在笔记二中我们已经知道了,在highgui文件夹下的正是opencv图形用户接口功能结构,我们这篇博客所说的便是D:\Program Files\opencv340\opencv\build\incl ...
 - AM335x(TQ335x)学习笔记——Nand&&网卡驱动移植
		
移植完毕声卡驱动之后本想再接再励,移植网卡驱动,但没想到的是TI维护的内核太健壮,移植网卡驱动跟之前移植按键驱动一样简单,Nand驱动也是如此,于是,本人将Nand和网卡放在同一篇文章中介绍.介绍之前 ...
 - Java学习笔记:GUI基础
		
一:我们使用到的java GUI的API可以分为3种类: 组件类(component class) 容器类(container class) 辅助类(helper class) 1:组件类:组件类是用 ...
 - MySQL学习笔记(六)MySQL8.0 配置笔记
		
今天把数据库配置文件修改了,结果重启不了了 需要使用 mysqld --initialize 或 mysqld --initialize-insecure 命令来初始化数据库 1.mysqld --i ...
 - shell编程学习笔记之特殊变量($0、$1、$2、 $?、 $# 、$@、 $*)
		
特殊变量($0.$1.$2. $?. $# .$@. $*) shell编程中有一些特殊的变量可以使用.这些变量在脚本中可以作为全局变量来使用. 名称 说明 $0 脚本名称 $1-9 脚本执行时的参数 ...
 - 接口与协议学习笔记-USB协议_USB2.0_USB3.0不同版本(三)
		
USB(Universal Serial Bus)全称通用串口总线,USB为解决即插即用需求而诞生,支持热插拔.USB协议版本有USB1.0.USB1.1.USB2.0.USB3.1等,USB2.0目 ...
 - 【opencv学习笔记三】opencv3.4.0数据类型解释
		
opencv提供了多种基本数据类型,我们这里分析集中常见的类型.opencv的数据类型定义可以在D:\Program Files\opencv340\opencv\build\include\open ...
 - java学习笔记_BeatBox(GUI部分)
		
import java.awt.*; import javax.swing.*; public class BeatBox { JFrame theFrame; JPanel mainPanel; S ...
 
随机推荐
- JS逆向实战8——某网实战(基于golang-colly)
			
其实本章算不上逆向教程 只是介绍golang的colly框架而已 列表页分析 根据关键字搜索 通过抓包分析可知 下一页所请求的参数如下 上图标红的代表所需参数 所以其实我们真正需要的也就是Search ...
 - springboot前端向后端请求返回html语句
			
后端接口代码 @PostMapping("/service/confirmPay") @ResponseBody public GlobalResponse confirmPay( ...
 - Git 分支管理策略汇总
			
原文链接: Git 分支管理策略 最近,团队新入职了一些小伙伴,在开发过程中,他们问我 Git 分支是如何管理的,以及应该怎么提交代码? 我大概说了一些规则,但仔细想来,好像也并没有形成一个清晰规范的 ...
 - 【题解】CF919D Substring
			
题面传送门 解决思路: DP 与拓扑结合.\(f_{i,j}\) 表示到 \(i\) 位置 \(j\) 的最大次数. 将 \(a \sim z\) 转成数字 \(0\sim 25\) ,方便存储. 考 ...
 - 如何避免由 Web 字体引起的布局偏移
			
前言 一些布局上的完全加载前后的变化很容易解决:为动态元素预先分配正确的空间,在图像上使用宽度和高度属性,并优先考虑 HTML 文档中的可见元素.但是,导致布局偏移的还有一个难以解决的问题:无样式文本 ...
 - 系统启动后bond配置不生效问题定位
			
背景描述 为了适配新功能,裸金属服务的磁盘镜像中做了如下修改: dracut添加network, iscsi模块 grub添加rd.iscsi.firmware=1参数 删除网卡配置文件/etc/sy ...
 - 多点DMALL × Apache Kyuubi:构建统一SQL Proxy探索实践
			
伴随着国家产业升级的推进和云原生技术成熟,多点 DMALL 大数据技术也经历了从存算一体到存算分离的架构调整变迁.本文将从引入 Kyuubi 实现统一 SQL Proxy 的角度讲述这一探索实践的历程 ...
 - js this获取元素ID
			
<table id="cyyj_table" class="table01" cellpadding="5" cellspacing= ...
 - Kubernetes专栏 | 安装部署(一)
			
--随着云原生概念的普及,许多企业的业务纷纷上云,为了追求可靠性,稳定性,和弹性伸缩,提升资源利用率等需求.Kubernetes这个谷歌开源的容器编排平台已日益流行,被大家熟知和使用. 通常来说,Ku ...
 - day23 约束 & 锁 & 范式
			
考点: 乐观锁=>悲观锁=>锁 表与表的对应关系 一对一:学生与手机号,一个学生对一个手机号 一对多:班级与学生,一个班级对应多个学生 多对一: 多对多:学生与科目,一个学生对应多个科目, ...