【学习笔记】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 ...
随机推荐
- MyBatisPlus 常用知识点总结
@ 目录 完整的Mybatis-Plus项目 常用注解 设置表名(@TableName) 设置实体类字段 (@TableField) 通过 @TableField(fill=FieldFill.INS ...
- Mybatis 报错Mapper method 'xxx' has an unsupported return type
报错原因: 出现这种错误,说明sql语句执行成功,只是返回类型出了问题. 解决方法: insert.delete.update操作默认返回一个int类型的整数,将增删改的接口改成int或者void即可 ...
- Fidder 抓包工具
fiddler抓包原理 如上图本文一些 不重要 的鸡肋功能 自行百度 1. 安装与配置 1. 安装 安装地址https://www.telerik.com/download/fiddler可能有点慢 ...
- RabbitMq发布确认
RabbitMq发布确认 发布确认原理 生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被 ...
- nginx日志切割并备份
[root@lecode-pre55 bin]# cat nginx-log.sh #!bin/bash #auther:ansheng #desc: nginx日志备份,注意脚本中文件的路径. #备 ...
- python解释器下载与安装指导手册
python解释器下载与安装指导手册 1.python解释器 1.1下载地址 1 https://www.python.org/ 1.2.python解释器下载 1.3.python解释器主流版本 p ...
- vue 项目中,后端返回文件流,导出excel
之前写过文件流导出excel,这次直接把上次的代码拿过来复制粘贴,但是导出的表格里面没有数据,只显示undefined. 这是之前的代码 // api接口页面 // excel导出接口 export ...
- PDF、视频格式缩略图获取(pdf2img)
PDF.视频格式缩略图获取(pdf2img) 获取pdf缩略图 导入依赖: <dependency> <groupId>org.apache.pdfbox</groupI ...
- springboot接收前端传参的几种方式
1.通过HttpServletRequest接收,常用于获取请求头参数以及Cookie,适用于GET 和 POST请求方式,以下两种方式: @GetMapping("/demo1" ...
- Centos安装Nodejs简单方式
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时.本文主要讲的是如何在Linux即Centos上安装Nodejs的简单方式,有比设置环境变量更加简单的方式,那就是设 ...