feat(littlefs): add lv_fs_littlefs system as a driver (#4677)

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
hermitsoft
2023-10-20 22:37:45 +02:00
committed by Gabor Kiss-Vamosi
parent 17c580fad6
commit fa40b05191
8 changed files with 414 additions and 7 deletions

11
Kconfig
View File

@@ -964,6 +964,17 @@ menu "LVGL configuration"
default 0 default 0
depends on LV_USE_FS_FATFS depends on LV_USE_FS_FATFS
config LV_USE_FS_LITTLEFS
bool "File system on top of LittleFS"
config LV_FS_LITTLEFS_LETTER
int "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65)"
default 0
depends on LV_USE_FS_LITTLEFS
config LV_FS_LITTLEFS_CACHE_SIZE
int ">0 to cache this number of bytes in lv_fs_read()"
default 0
depends on LV_USE_FS_LITTLEFS
config LV_USE_PNG config LV_USE_PNG
bool "PNG decoder library" bool "PNG decoder library"

View File

@@ -2,14 +2,38 @@
# File System Interfaces # File System Interfaces
LVGL has a [File system](https://docs.lvgl.io/master/overview/file-system.html) module to provide an abstraction layer for various file system drivers. LVGL has a [File system](https://docs.lvgl.io/master/overview/file-system.html) module to provide an abstraction layer for various file system drivers.
You still need to provide the drivers and libraries, this extension provides only the bridge between FATFS, LittleFS, STDIO, POSIX, WIN32 and LVGL.
LVG has built in support for: ## Built in wrappers
- [FATFS](http://elm-chan.org/fsw/ff/00index_e.html)
- STDIO (Linux and Windows using C standard function .e.g fopen, fread)
- POSIX (Linux and Windows using POSIX function .e.g open, read)
- WIN32 (Windows using Win32 API function .e.g CreateFileA, ReadFile)
You still need to provide the drivers and libraries, this extension provides only the bridge between FATFS, STDIO, POSIX, WIN32 and LVGL. ### FATFS
Bridge for [FatFS](http://elm-chan.org/fsw/ff/00index_e.html). FatFS itself is not part of LVGL, but can be added and initialized externally.
### LittleFS
Though `lv_fs_littlefs` uses [LittleFS]((https://github.com/littlefs-project/littlefs)) API, the LittleFS library needs other external libraries that handle the mounting of partitions and low-level accesses, according to the given architecture. The functions for the latter are given to the lfs_t structure as pointers by an external low-level library.
There's a convenience function called `lv_fs_littlefs_set_driver(LV_FS_LITTLEFS_LETTER, my_lfs)`, specific to `lv_fs_littlefs`, to attach a `lfs_t` object's pointer to a registered driver-letter. See its comments for more info.
[esp_littlefs](https://components.espressif.com/components/joltwallet/littlefs) is a wrapper for LittleFS to be used in Espressif ESP-devices. It handles the mounting and has the low-level `littlefs_api` functions to read/write/erase blocks that LittleFS library needs. On mounting by `esp_littlefs` the `lfs_t` structures are created. You need to get a handle to these to use ESP with `lv_fs_littlefs`, as all functions use that `lfs_t` in LittleFS to identify the mounted partition.
In case you don't find a special function in the `lv_fs_littlefs` wrapper, you can look for it in the `esp_littlefs` API and use it directly, as `lv_fs_littlefs` and the `esp_littlefs` APIs can be used side-by-side.
### STDIO
Bride to C standard functions on Linux and Windows. For example `fopen`, `fread`, etc.
### POSIX
Bride to POSIX functions on Linux and Windows. For example `open`, `read`, etc.
### WIN32
Bride to Win32 API function. For example `CreateFileA`, `ReadFile`, etc.
## Usage ## Usage

View File

@@ -639,6 +639,13 @@
#define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif #endif
/*API for LittleFS (library needs to be added separately). Uses lfs_file_open, lfs_file_read, etc*/
#define LV_USE_FS_LITTLEFS 0
#if LV_USE_FS_LITTLEFS
#define LV_FS_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_LITTLEFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*PNG decoder library*/ /*PNG decoder library*/
#define LV_USE_PNG 0 #define LV_USE_PNG 0

View File

@@ -0,0 +1,332 @@
/**
* @file lv_fs_littlefs.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_LITTLEFS
#include "lfs.h"
/*********************
* DEFINES
*********************/
#if LV_FS_LITTLEFS_LETTER == '\0'
#error "LV_FS_LITTLEFS_LETTER must be an upper case ASCII letter"
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void fs_init(void);
static void * fs_open(lv_fs_drv_t * drv, 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, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_fs_littlefs_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; /*A driver descriptor*/
lv_fs_drv_init(&fs_drv);
/*Set up fields...*/
fs_drv.letter = LV_FS_LITTLEFS_LETTER;
fs_drv.cache_size = LV_FS_LITTLEFS_CACHE_SIZE;
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_close_cb = fs_dir_close;
fs_drv.dir_read_cb = fs_dir_read;
/*#if LV_USE_USER_DATA*/
fs_drv.user_data = NULL;
/*#endif*/
lv_fs_drv_register(&fs_drv);
}
/**
* Convenience function to attach registered driver to lfs_t structure by driver-label
* @param label the label assigned to the driver when it was registered
* @param lfs_p the pointer to the lfs_t structure initialized by external code/library
* @return pointer to a driver descriptor or NULL on error
*/
lv_fs_drv_t * lv_fs_littlefs_set_driver(char label, void * lfs_p)
{
lv_fs_drv_t * drv_p = lv_fs_get_drv(label);
if(drv_p != NULL) drv_p->user_data = (lfs_t *) lfs_p;
return drv_p;
}
/**********************
* STATIC FUNCTIONS
**********************/
/*Initialize your Storage device and File system.*/
static void fs_init(void)
{
/* Initialize the internal flash or SD-card and LittleFS itself.
* Better to do it in your code to keep this library untouched for easy updating */
}
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
* @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
* @return pointer to a file descriptor or NULL on error
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
lfs_t * lfs_p = drv->user_data;
uint32_t flags = 0;
flags = mode == LV_FS_MODE_RD ? LFS_O_RDONLY
: mode == LV_FS_MODE_WR ? LFS_O_WRONLY
: mode == (LV_FS_MODE_WR | LV_FS_MODE_RD) ? LFS_O_RDWR : 0;
lfs_file_t * file_p = lv_mem_alloc(sizeof(lfs_file_t));
if(file_p == NULL) return NULL;
int result = lfs_file_open(lfs_p, file_p, path, flags);
if(result != LFS_ERR_OK) {
lv_mem_free(file_p);
return NULL;
}
return file_p;
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
lfs_t * lfs_p = drv->user_data;
int result = lfs_file_close(lfs_p, file_p);
lv_mem_free(file_p);
/*lv_mem_free( lfs_p );*/ /*allocated and freed by outside-code*/
if(result != LFS_ERR_OK) return LV_FS_RES_UNKNOWN;
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
lfs_t * lfs_p = drv->user_data;
lfs_ssize_t result = lfs_file_read(lfs_p, file_p, buf, btr);
if(result < 0) return LV_FS_RES_UNKNOWN;
*br = (uint32_t) result;
return LV_FS_RES_OK;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable
* @param buf pointer to a buffer with the bytes to write
* @param btw Bytes To Write
* @param bw the number of real written bytes (Bytes Written). NULL if unused.
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
#ifndef LFS_READONLY
lfs_t * lfs_p = drv->user_data;
lfs_ssize_t result = lfs_file_write(lfs_p, file_p, buf, btw);
if(result < 0 || lfs_file_sync(lfs_p, file_p) < 0) return LV_FS_RES_UNKNOWN;
*bw = (uint32_t) result;
return LV_FS_RES_OK;
#else
return LV_FS_RES_NOT_IMP;
#endif
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @param whence tells from where to interpret the `pos`. See @lv_fs_whence_t
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
lfs_t * lfs_p = drv->user_data;
int lfs_whence = whence == LV_FS_SEEK_SET ? LFS_SEEK_SET
: whence == LV_FS_SEEK_CUR ? LFS_SEEK_CUR
: whence == LV_FS_SEEK_END ? LFS_SEEK_END : 0;
lfs_soff_t result = lfs_file_seek(lfs_p, file_p, pos, lfs_whence);
if(result < 0) return LV_FS_RES_UNKNOWN;
/*pos = result;*/ /*not supported by lv_fs*/
return LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable.
* @param pos_p pointer to where to store the result
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
lfs_t * lfs_p = drv->user_data;
lfs_soff_t result = lfs_file_tell(lfs_p, file_p);
if(result < 0) return LV_FS_RES_UNKNOWN;
*pos_p = (uint32_t) result;
return LV_FS_RES_OK;
}
/**
* Initialize a 'lv_fs_dir_t' variable for directory reading
* @param drv pointer to a driver where this function belongs
* @param path path to a directory
* @return pointer to the directory read descriptor or NULL on error
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
lfs_t * lfs_p = drv->user_data;
lfs_dir_t * dir_p = lv_mem_alloc(sizeof(lfs_dir_t));
if(dir_p == NULL) return NULL;
int result = lfs_dir_open(lfs_p, dir_p, path);
if(result != LFS_ERR_OK) {
lv_mem_free(dir_p);
return NULL;
}
return dir_p;
}
/**
* Read the next filename form a directory.
* The name of the directories will begin with '/'
* @param drv pointer to a driver where this function belongs
* @param rddir_p pointer to an initialized 'lv_fs_dir_t' variable
* @param fn pointer to a buffer to store the filename
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * rddir_p, char * fn)
{
lfs_t * lfs_p = drv->user_data;
struct lfs_info info;
int result;
info.name[0] = '\0';
do {
result = lfs_dir_read(lfs_p, rddir_p, &info);
if(result > 0) {
if(info.type == LFS_TYPE_DIR) {
fn[0] = '/';
strcpy(&fn[1], info.name);
}
else strcpy(fn, info.name);
}
else if(result == 0) fn[0] = '\0'; /*dir-scan ended*/
else return LV_FS_RES_UNKNOWN;
} while(!strcmp(fn, "/.") || !strcmp(fn, "/.."));
return LV_FS_RES_OK;
}
/**
* Close the directory reading
* @param drv pointer to a driver where this function belongs
* @param rddir_p pointer to an initialized 'lv_fs_dir_t' variable
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * rddir_p)
{
lfs_t * lfs_p = drv->user_data;
int result = lfs_dir_close(lfs_p, rddir_p);
lv_mem_free(rddir_p);
if(result != LFS_ERR_OK) return LV_FS_RES_UNKNOWN;
return LV_FS_RES_OK;
}
#else /*LV_USE_FS_LITTLEFS == 0*/
#if defined(LV_FS_LITTLEFS_LETTER) && LV_FS_LITTLEFS_LETTER != '\0'
#warning "LV_USE_FS_LITTLEFS is not enabled but LV_FS_LITTLEFS_LETTER is set"
#endif
#endif /*LV_USE_FS_POSIX*/

View File

@@ -31,6 +31,11 @@ extern "C" {
void lv_fs_fatfs_init(void); void lv_fs_fatfs_init(void);
#endif #endif
#if LV_USE_FS_LITTLEFS != '\0'
void lv_fs_littlefs_init(void);
lv_fs_drv_t * lv_fs_littlefs_set_driver(char label, void * lfs_p);
#endif
#if LV_USE_FS_STDIO != '\0' #if LV_USE_FS_STDIO != '\0'
void lv_fs_stdio_init(void); void lv_fs_stdio_init(void);
#endif #endif

View File

@@ -50,6 +50,10 @@ void lv_extra_init(void)
lv_fs_fatfs_init(); lv_fs_fatfs_init();
#endif #endif
#if LV_USE_FS_LITTLEFS != '\0'
lv_fs_littlefs_init();
#endif
#if LV_USE_FS_STDIO != '\0' #if LV_USE_FS_STDIO != '\0'
lv_fs_stdio_init(); lv_fs_stdio_init();
#endif #endif

View File

@@ -2099,6 +2099,31 @@
#endif #endif
#endif #endif
/*API for LittleFS (library needs to be added separately). Uses lfs_file_open, lfs_file_read, etc*/
#ifndef LV_USE_FS_LITTLEFS
#ifdef CONFIG_LV_USE_FS_LITTLEFS
#define LV_USE_FS_LITTLEFS CONFIG_LV_USE_FS_LITTLEFS
#else
#define LV_USE_FS_LITTLEFS 0
#endif
#endif
#if LV_USE_FS_LITTLEFS
#ifndef LV_FS_LITTLEFS_LETTER
#ifdef CONFIG_LV_FS_LITTLEFS_LETTER
#define LV_FS_LITTLEFS_LETTER CONFIG_LV_FS_LITTLEFS_LETTER
#else
#define LV_FS_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#endif
#endif
#ifndef LV_FS_LITTLEFS_CACHE_SIZE
#ifdef CONFIG_LV_FS_LITTLEFS_CACHE_SIZE
#define LV_FS_LITTLEFS_CACHE_SIZE CONFIG_LV_FS_LITTLEFS_CACHE_SIZE
#else
#define LV_FS_LITTLEFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
#endif
#endif
/*PNG decoder library*/ /*PNG decoder library*/
#ifndef LV_USE_PNG #ifndef LV_USE_PNG
#ifdef CONFIG_LV_USE_PNG #ifdef CONFIG_LV_USE_PNG

View File

@@ -57,7 +57,6 @@ enum {
}; };
typedef uint8_t lv_fs_mode_t; typedef uint8_t lv_fs_mode_t;
/** /**
* Seek modes. * Seek modes.
*/ */