小熊派 LVGL 移植文件系统
一、移植前言
之前使用小熊派实现了鸿蒙动画的开机界面,具体使用的技术栈为 STM32 + LiteOS + LVGL + FATFS +DMA 方式实现,刷新效率非常高,预览视频如下:
关于这个的实现过程我会写一系列的教程分享出来,主要分为下面几个部分,本节为第三部分,给 LVGL 移植 FATFS 文件系统接口
- 小熊派移植华为 LiteOS-M(基于MDK):链接;
- 小熊派基于 LiteOS 移植 LVGL 显示接口:链接;
- 小熊派基于 LiteOS 移植 LVGL 文件系统:链接;
- 小熊派实现鸿蒙开机界面(LiteOS+LVGL):链接;
本节的教程基于上一节配置好的工程,使用 STM32CubeMX 进一步配置 SDIO 总线接口,同时移植 FATFS 文件系统,关于 CubeMX 配置 SDIO 的教程,可以参考 mculover666 大神的:
【STM32Cube_20】在SD卡上移植FATFS文件系统
二、CubeMX 开启 SDIO 接口
开启 SD 单线模式
配置 SDIO 参数(保持默认):
三、CubeMX 开启 FATFS
在开启 SD 卡后,我们添加 FATFS 的文件系统支持
参数保持默认,mculover666 教程中开启了 FATFS 对文件中文名称的支持,但会占据很大的 rom 空间(180k),所以我们就不开启了,直接使用英文名称
配置完成后生成代码,这里生成代码我们要修改一下栈的大小,因为 FATFS 和 LVGL 都使用到了栈,所以改大一些
生成代码,有个警告,就是有些没用到的参数没配置,我们不管他
四、LVGL对接FATFS
代码生成了,我们先编写个小 Demo 测试一下 FAT 有没有移植成功,主函数插入如下代码:
定义一个文件系统对象和返回值
/* USER CODE BEGIN PV */
FATFS fs; /* FATFS 文件系统对象 */
FRESULT fr; /* FATFS API 返回值 */
/* USER CODE END PV */
在初始化前添加如下代码
/* USER CODE BEGIN 2 */
printf("FATFS test...\r\n");
/* 挂载SD卡 */
fr = f_mount(&fs, "", 0);
if(fr == FR_OK)
{
printf("SD card mount ok!\r\n");
}
else
{
printf("SD card mount error, error code:%d.\r\n",fr);
}
编译下载,观察串口输出:
测试正常,有的时候运行程序时会出现内存分配不够的报错,可能是因为 LiteOS 内存管理设置没有配置好,导致 LVGL 的内存池不够分配 ,LiteOS 系统启动需要手动设置系统 SRAM 的起止地址以及空间大小,因为我使用的是 MDK IDE,LiteOS 不能获得芯片 RAM 大小,所以我们把它改成 64k 就行:
当 FATFS 测试成功后,下一个就是移植到 LVGL 上,移植文件系统过程和移植显示接口过程很相似,先使能 lv_port_fs.c 和 .h 里面的宏定义
同时在 lv_conf.h 里面开启文件系统使能
编译一下代码,通过
下面就是修改 fs 文件接口了,进入 lv_port_fs.c 文件,看到 lv_port_fs_init 函数,函数里面是有关文件系统的接口初始化,代码如下
void lv_port_fs_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 */
lv_fs_drv_t fs_drv;
lv_fs_drv_init(&fs_drv);
/*Set up fields...*/
fs_drv.file_size = sizeof(file_t);
fs_drv.letter = 'P';
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.free_space_cb = fs_free;
fs_drv.size_cb = fs_size;
fs_drv.remove_cb = fs_remove;
fs_drv.rename_cb = fs_rename;
fs_drv.trunc_cb = fs_trunc;
fs_drv.rddir_size = sizeof(dir_t);
fs_drv.dir_close_cb = fs_dir_close;
fs_drv.dir_open_cb = fs_dir_open;
fs_drv.dir_read_cb = fs_dir_read;
lv_fs_drv_register(&fs_drv);
}
fs_init() 是初始化 SD 卡和文件系统接口,这里初始化接口我们已经在 CubeMX 生成的代码中初始化过了,所以不用再初始化,只需要在改代码里面放上挂载 SD 卡设备代码就行,下面的代码就是建立一个文件系统驱动
/* Add a simple drive to open images */
lv_fs_drv_t fs_drv;
lv_fs_drv_init(&fs_drv);
然后配置驱动的函数入口
/*Set up fields...*/
fs_drv.file_size = sizeof(file_t);
fs_drv.letter = 'P';
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.free_space_cb = fs_free;
fs_drv.size_cb = fs_size;
fs_drv.remove_cb = fs_remove;
fs_drv.rename_cb = fs_rename;
fs_drv.trunc_cb = fs_trunc;
fs_drv.rddir_size = sizeof(dir_t);
fs_drv.dir_close_cb = fs_dir_close;
fs_drv.dir_open_cb = fs_dir_open;
fs_drv.dir_read_cb = fs_dir_read;
最后注册驱动
lv_fs_drv_register(&fs_drv);
注册完成后,lvgl 会根据注册驱动的函数入口去执行对应的函数,我们只要修改对应的函数就行,这里我们先修改我们使用到的函数主要是
fs_open //打开文件
fs_close //关闭文件
fs_read //读文件
fs_write //写文件
fs_seek //重定位文件
下面修改这些函数,现在头部包含头文件,然后重新定义文件类型
添加头文件
/*********************
* INCLUDES
*********************/
#include "lv_port_fs.h"
#include "fatfs.h"
#include "usart.h"
覆盖掉原型的类型定义
/**********************
* TYPEDEFS
**********************/
typedef FIL file_t; // 把FIL类型定义成file_t
typedef DIR dir_t; // 把DIR类型定义成dir_t
修改 fs_init() 函数
static void fs_init(void)
{
/*E.g. for FatFS initialize the SD card and FatFS itself*/
static FATFS fs; /* FATFS 文件系统对象 */
FRESULT fr; /* FATFS API 返回值 */
printf("LVGL FATFS INIT...\r\n");
fr = f_mount(&fs, "", 0);
if (fr != FR_OK)
{
printf("SD Card mounted error: (%d)\r\n", fr);
} else
printf("SD Card mounted successfully!\r\n");
/*You code here*/
}
修改打开文件函数:
static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode)
{
lv_fs_res_t res = LV_FS_RES_NOT_IMP;
FRESULT fr; /* FATFS API 返回值 */
if(mode == LV_FS_MODE_WR)
{
/*Open a file for write*/
fr = f_open((FIL*)file_p, path, LV_FS_MODE_WR);
if(fr != FR_OK)
{
printf("open file \"%s\" error : %d\r\n", path, fr);
}else
res = LV_FS_RES_OK;
}
else if(mode == LV_FS_MODE_RD)
{
/*Open a file for read*/
fr = f_open((FIL*)file_p, path, LV_FS_MODE_RD);
if(fr != FR_OK)
{
printf("open file \"%s\" error : %d\r\n", path, fr);
}else
res = LV_FS_RES_OK;
}
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD))
{
/*Open a file for read and write*/
fr = f_open((FIL*)file_p, path, LV_FS_MODE_WR | LV_FS_MODE_RD);
if(fr != FR_OK)
{
printf("open file \"%s\" error : %d\r\n", path, fr);
}else
res = LV_FS_RES_OK;
}
return res;
}
关闭文件接口
static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p)
{
lv_fs_res_t res = LV_FS_RES_NOT_IMP;
FRESULT fr; /* FATFS API 返回值 */
printf("close file\r\n");
/* 操作完成,关闭文件 */
fr = f_close((FIL*)file_p);
if(fr != FR_OK)
{
printf("close file error! error : %d\r\n", fr);
}else
res = LV_FS_RES_OK;
return res;
}
定位文件接口
static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos)
{
lv_fs_res_t res = LV_FS_RES_NOT_IMP;
FRESULT fr; /* FATFS API 返回值 */
fr = f_lseek((FIL*)file_p, pos);
if (fr != FR_OK)
{
printf("read file error! error : %d\r\n", fr);
}else
res = LV_FS_RES_OK;
return res;
}
读写文件接口
static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
lv_fs_res_t res = LV_FS_RES_NOT_IMP;
FRESULT fr; /* FATFS API 返回值 */
fr = f_read((FIL*)file_p, buf, btr, br);
if (fr != FR_OK)
{
printf("read file error! error : %d\r\n", fr);
}else
res = LV_FS_RES_OK;
return res;
}
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
lv_fs_res_t res = LV_FS_RES_NOT_IMP;
FRESULT fr; /* FATFS API 返回值 */
fr = f_write((FIL*)file_p, buf, btw, bw);
if (fr != FR_OK)
{
printf("read file error! error : %d\r\n", fr);
}else
res = LV_FS_RES_OK;
return res;
}
到此移植文件系统接口基本上完成,下面编写一个测试代码
五、测试代码
在lcd任务中添加如下代码,打开 SD 卡下面的 lvgl.txt 文件,写入 test 字符串
lv_port_fs_init();
lv_fs_file_t *file1 = lv_mem_alloc(sizeof(lv_fs_file_t *));
fs_res=lv_fs_open(file1,"S:/lvgl.txt",LV_FS_MODE_WR | LV_FS_MODE_RD);
if(fs_res != LV_FS_RES_OK)
printf("open error! code:%d\r\n",fs_res);
fs_res=lv_fs_write(file1,"test",4,NULL);
if(fs_res != LV_FS_RES_OK)
printf("write error! code:%d\r\n",fs_res);
fs_res=lv_fs_close(file1);
if(fs_res != LV_FS_RES_OK)
printf("close error! code:%d\r\n",fs_res);
lv_mem_free(file1);
下载编译
六、实验现象
串口返回写入成功:
打开sd卡下面的文件,观察写入情况,写入成功: