STM32在LVGL上实现移植FatFs文件系统(保姆级详细教程)

一、移植背景

硬件平台:STM32H743

软件平台:已经移植好了LVGL_v8.3.11和FatFs_v0.16的工程

本文结尾有彩蛋哦!!!~

二、移植前准备

1.确保你的LVGL已经移植好并且正常显示GUI。

下面是STM32的LVGL保姆级移植教程:

STM32 在Keil 5 下移植LVGL_v8.3.11(步骤堪称保姆级,致力打造史上最详细lvgl移植教程,小白上手即可实现100%成功移植!!!)-CSDN博客https://blog.csdn.net/qq_34885669/article/details/149667437?spm=1001.2014.3001.5502

2.确保你的FatFs已经移植好并且正常读写SD卡。

下面是STM32的FatFs保姆级移植教程:

保姆级移植教程待更新。。。。(这个教程资料比较成熟,建议网上找)

由于本文重点是实现LVGL上移植FatFs ,所以LVGLFatFs各自的移植就不在这里去介绍了,请自行查看上面的教程,把LVGL和FatFs移植好之后再来看本文章后面的内容。

备注:

LVGLFatFs各自的移植意思就是:你要先把LVGL移植到你带屏幕的工程里面并且实现LVGL显示GUI正常 ,然后要把FatFs和你的SD卡驱动移植对接好,确保你移植的FatFs可以正常打开读写SD卡里面的文件

实现LVGL上移植FatFs意思就是:把LVGL 自己的文件系统抽象模块 lv_fs和FatFs的标准 文件系统APIf_open, f_read, f_close, f_opendir, f_readdir, f_stat 移植在一起,实现LVGL可以通过自己的文件系统API来操作SD卡里面的内容,比如使用LVGL显示SD卡里面的图片。

三、LVGL 文件系统抽象层API移植

所谓的LVGL 文件系统抽象层API移植,简单来讲就是把FatFs的文件系统操作API和LVGL 文件系统抽象层API对接在一起,实现LVGL可以通过自己的文件系统API来操作SD卡里面的内容。 和之前LVGL移植适配屏幕驱动一样,LVGL文件系统一样是有一个lv_port_fs.c这样的文件系统适配层文件,接下来我们就是要对这个文件做移植适配。

备注:在LVGL源码的examples\porting路径下就是LVGL官方给出的显示,输入,文件系统移植接口适配文件示例,记得要添加到你的工程里面。

1.启用lv_port_fs.c

在使用#if 1启用lv_port_fs.c后,我们还需要添加好FatFs文件系统头文件和MCU一下你用到的头文件:

2.注册FatFs文件系统文件操作API

找到lv_port_fs.c的lv_port_fs_init函数,如图:

lv_port_fs_init它的作用简单来说就是把LVGL自己的文件操作回调接口和FatFs的文件操作API做一下一一对应,然后再将对应好的接口注册到LVGL里面,这样LVGL在打开SD卡里面的文件的时候就会调用到我们对应好的FatFs文件系统操作API去读写SD卡里面的文件。

这个函数我们需要改动的其实很少,只需要设置一下fs_drv.letter

注册文件系统接口时设置的fs_drv.letter这个所谓的盘符 ,就是在LVGL层面设置文件路径时用到的盘符 ,但是实际上FatFs在对接SD卡时也有一个盘符,严格来说是FatFs的驱动器号。这个盘符也是移植中最容易出问题的地方,我下面举一个例子你们便一目了然了:

我在移植FatFs时给SD卡定义的驱动器号为1

假设:在SD卡的根目录里面有个BMP文件夹,里面有个test.bmp图片,fs_drv.letter = 'S'。diskio.c文件里SD卡驱动器号为1。

那么:

LVGL层面显示照片时调用lv_img_set_src设置图片路径应该使用:S:/BMP/test.bmp

FatFs层面的盘符却一般都是0~9这样的数字,在FatFs里面叫驱动器号,假设SD卡驱动器号是1,即在FatFs层面这个图片的路径应该是:1:/BMP/test.bmp

总结来说:

当你使用LVGL层面的API去操作SD卡里面的文件,那就得使用LVGL的fs_drv.letter开头的路径,比如S:/BMP/test.bmp

当你使用FatFs层面的API去操作SD卡里面的文件,那就得使用FatFs的驱动器号开头的路径,比如1:/BMP/test.bmp

3.fs_init文件系统文件操作API移植

fs_init里面其实主要就是调用FatFs层面的f_mount去挂载SD卡,我们要注意的就是f_mount的第二个参数要填的是SD卡对应的驱动器号不是****LVGL层面的fs_drv.letter。

复制代码
static void fs_init(void)
{
    /*E.g. for FatFS initialize the SD card and FatFS itself*/

    /*You code here*/
	static FATFS fs;   // 文件系统对象
	// 挂载 FATFS
    FRESULT res = f_mount(&fs, "1:", 1);
    if(res != FR_OK) 
	{
        printf("LVGL fatfs mount failed: %d\r\n", res);
    }
	else
	{
		printf("LVGL fatfs mount succeed: %d\r\n", res);
	}
}

4.fs_open文件系统文件操作API移植

移植fs_open,我们主要要注意的就是2点:文件操作模式和文件路径 的适配移植。

需要注意的是, LVGL 传进来的path已经没有盘符,比如 "BMP/test.bmp" ,我们需要把盘符补充上去,变成"1:/BMP/test.bmp"然后才能给f_open使用:

复制代码
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;
	BYTE fatfs_mode = 0;
	/*把LVGL的打开文件的模式和FatFs一一对应好*/
    if(mode == LV_FS_MODE_WR) fatfs_mode = FA_WRITE | FA_OPEN_ALWAYS;
    else if(mode == LV_FS_MODE_RD) fatfs_mode = FA_READ;
    else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) fatfs_mode = FA_READ | FA_WRITE | FA_OPEN_ALWAYS;
	
	/* LVGL 传进来的path已经没有盘符,比如 "test.txt" ,我们需要把盘符补充上去,然后才能给f_open使用*/
    char *full_path = lv_mem_alloc(256);
	if(full_path == NULL) return NULL;
	lv_snprintf(full_path, 256, "1:%s", path);  // 注意 path 已经是 "/xxx"
	//printf("fs_open full_path=%s \n",full_path);
	FIL * f = lv_mem_alloc(sizeof(FIL));   // 为每个文件分配空间
    if(f == NULL)
	{
		printf("lv_mem_alloc failed ! (f == NULL)\r\n");
		return NULL;
	}
	FRESULT fr = f_open(f, full_path, fatfs_mode);
	if(fr == FR_OK) 
	{
		printf("fs_open full_path=%s  OK !\n",full_path);
        return f;
    }
	else 
	{
		printf("f_open failed: %d, full_path=%s\n", fr, full_path);
        lv_mem_free(f);
        return NULL;
    }
}

5.fs_close文件系统文件操作API移植

复制代码
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
	LV_UNUSED(drv);
	if(FR_OK == f_close((FIL*)file_p)) 
	{
		lv_mem_free(file_p);
		return LV_FS_RES_OK;
	}
    else
	{
		lv_mem_free(file_p);
		return LV_FS_RES_UNKNOWN;
	}
}

6.fs_read文件系统文件操作API移植

复制代码
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
    /*Add your code here*/
	LV_UNUSED(drv);

    FRESULT res = f_read(file_p, buf, btr, (UINT *)br);
    if(res == FR_OK) return LV_FS_RES_OK;
    else return LV_FS_RES_UNKNOWN;
}

7.fs_write文件系统文件操作API移植

复制代码
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
    /*Add your code here*/
	 LV_UNUSED(drv);
    FRESULT res = f_write(file_p, buf, btw, (UINT *)bw);
    if(res == FR_OK) return LV_FS_RES_OK;
    else return LV_FS_RES_UNKNOWN;
}

8.fs_seek文件系统文件操作API移植

复制代码
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
    /*Add your code here*/
	LV_UNUSED(drv);
    switch(whence) {
        case LV_FS_SEEK_SET:
            f_lseek(file_p, pos);
            break;
        case LV_FS_SEEK_CUR:
            f_lseek(file_p, f_tell((FIL *)file_p) + pos);
            break;
        case LV_FS_SEEK_END:
            f_lseek(file_p, f_size((FIL *)file_p) + pos);
            break;
        default:
            break;
    }
    return LV_FS_RES_OK;
}

9.fs_tell文件系统文件操作API移植

复制代码
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
    /*Add your code here*/
	LV_UNUSED(drv);
    *pos_p = f_tell((FIL *)file_p);
    return LV_FS_RES_OK;
}

10.fs_dir_open文件系统文件操作API移植

复制代码
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
	 LV_UNUSED(drv);
    DIR * d = lv_mem_alloc(sizeof(DIR));
    if(d == NULL) return NULL;
	
	/*Make the path relative to the current directory (the projects root folder)*/
	char *real_path = lv_mem_alloc(256);
	if(real_path == NULL) return NULL;
    lv_snprintf(real_path, 256, "1:%s", path);
    FRESULT res = f_opendir(d, real_path);
    if(res != FR_OK) {
        lv_mem_free(d);
        d = NULL;
    }
    return d;
}

10.fs_dir_close文件系统文件操作API移植

复制代码
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * rddir_p)
{
	LV_UNUSED(drv);
	if(FR_OK == f_closedir(rddir_p)) 
	{
		lv_mem_free(rddir_p);
		return LV_FS_RES_OK;
	}
    else
	{
		lv_mem_free(rddir_p);
		return LV_FS_RES_UNKNOWN;
	}
}

11.fs_dir_read文件系统文件操作API移植

复制代码
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * rddir_p, char * fn)
{
	LV_UNUSED(drv);
    FRESULT res;
    FILINFO fno;
    fn[0] = '\0';
    do {
        res = f_readdir(rddir_p, &fno);
        if(res != FR_OK) return LV_FS_RES_UNKNOWN;

        if(fno.fattrib & AM_DIR) {
            fn[0] = '/';
            strcpy(&fn[1], fno.fname);
        }
        else strcpy(fn, fno.fname);

    } while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);

    return LV_FS_RES_OK;
}

三、初始化LVGL的文件系统

最后,我们需要在main函数调用lv_port_fs_init();来初始化我们移植好的FatFs:

四、验证测试

最简单的我们就是可以使用显示SD卡里面的图片来测试移植是否成功:

复制代码
	/* show image */
	lv_obj_t * img1 = lv_img_create(lv_scr_act());
	lv_img_set_src(img1, "S:/JPEG/girl.jpg");
	lv_obj_center(img1);

在main.c里面调用lv_port_fs_init(); 初始化FatFs文件系统:

需要注意的是:如果你想测试显示SD卡里面的图片,必须先在lv_conf.h里面启用对应的图片编解码器

到此为止,移植结束!!!

彩蛋:

实际上,LVGL8.3.11它自带的源码里面就已经移植好了FatFs!!!我们只需要去lv_conf.h里面配置一下相关的宏定义然后简单修改下源码就可以了。哈哈哈哈!!!

在源码包里面的**\src\extra\libs\fsdrv\lv_fs_fatfs.c****就是LVGL官方已经适配好的FatFs适配层。**

使用方法:

1.把lv_fs_fatfs.c添加到你的工程里面

2.打开lv_conf.h 找到:

同理,开启LV_USE_FS_FATFS,然后设置好LV_FS_FATFS_LETTERLV_FS_FATFS_PATH****如图:

然后打开LVGL源码自带的**lv_fs_fatfs.c,找到fs_init,**添加一下挂载文件系统的代码:

只需要在lv_fs_fatfs.c的****fs_open接口里面添加一下补充盘符代码就可以了:

同理在lv_fs_fatfs.c的****fs_dir_open 接口里面添加一下补充盘符代码

main.c里面的 初始化文件系统改为使用lv_fs_fatfs_init();

相关推荐
Jerry丶Li2 小时前
二十二、STM32的ADC(二)(ADC单通道)
stm32·单片机·嵌入式硬件
飞睿科技2 小时前
超越蓝牙与Wi-Fi,UWB技术如何解锁手机下一波创新浪潮?
嵌入式硬件·物联网·智能手机·uwb
点灯小铭2 小时前
基于单片机的交流功率测量仪设计与实现
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
星辰pid4 小时前
STM32基于OLED的多级菜单(控制步进/无刷电机/舵机,含FLASH存储数据掉电不丢失)
stm32·单片机·嵌入式硬件
飞睿科技5 小时前
乐鑫ESP32-C2小尺寸高性价比,物联网应用的理想无线连接方案
嵌入式硬件·物联网·智能路由器
RFID舜识物联网5 小时前
NFC与RFID防伪标签:构筑产品信任的科技防线
大数据·人工智能·科技·嵌入式硬件·物联网·安全
FanXing_zl5 小时前
在整数MCU上实现快速除法计算:原理、方法与优化
单片机·嵌入式硬件·mcu·算法·定点运算
Dunkle.T7 小时前
单片机中NRST引脚复用为GPIO
单片机·嵌入式硬件·复用·py32f002bl15s7·nrst
逆小舟7 小时前
【STM32】中断
stm32·单片机·嵌入式硬件