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();

相关推荐
SundayBear9 小时前
零基础入门MQTT协议
c语言·单片机
嗯嗯=10 小时前
STM32单片机学习篇9
stm32·单片机·学习
小范馆14 小时前
ESP各模组的引脚图-小智接线图
stm32
松涛和鸣15 小时前
DAY63 IMX6ULL ADC Driver Development
linux·运维·arm开发·单片机·嵌入式硬件·ubuntu
想放学的刺客18 小时前
单片机嵌入式试题(第23期)嵌入式系统电源管理策略设计、嵌入式系统通信协议栈实现要点两个全新主题。
c语言·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆18 小时前
【Linux 驱动开发】五. 设备树
linux·arm开发·驱动开发·stm32·嵌入式硬件·mcu·硬件工程
YouEmbedded19 小时前
解码内部集成电路(IIC)与OLED屏
stm32·0.96寸oled·硬件iic·软件模拟iic·图片取模·汉字取模
jghhh0119 小时前
基于上海钜泉科技HT7017单相计量芯片的参考例程实现
科技·单片机·嵌入式硬件
恶魔泡泡糖20 小时前
51单片机外部中断
c语言·单片机·嵌入式硬件·51单片机
意法半导体STM3220 小时前
【官方原创】如何基于DevelopPackage开启安全启动(MP15x) LAT6036
javascript·stm32·单片机·嵌入式硬件·mcu·安全·stm32开发