STM32轻松移植LVGL官方PACK包,一键导入!再也不用担心移植失败啦!!!
在2024年6月,LVGL与ARM公司展开合作,LVGL移植与应用起来就比较方便了,以前移植LVGL的方法很繁琐,需要去LVGL的GitHub里面拉取文件,然后就是一系列繁琐的移植操作,大部分的伙伴都是因为各种移植的问题而苦恼。这边我采用的方法就比较简单粗暴了,直接运用ARM公司推出的PACK包,这样就不会因为各种文件移植而苦恼了,接下来将带领各位伙伴们轻松移植LVGL。
第一步:
打开ARM Keil官网Arm Keil | LVGL lvgl
官网当中有各个版本的PACK包,可以根据需要去选择,可以选几个稳定的版本下载,不是版本越高越好,比如我都是最高版本9.0当中就有一些BUG,后面出小版本迭代之后,会逐步解决这些BUG,推荐下载比较稳定的版本,后续在项目当中可以替换更新的版本。
第二步:
将下载的PACK导入到Keil中
点开下载好的PACK包,按照提示导入到Keil当中,如有多个版本的PACK包,依次点击导入
打开Keil的点击PACK的管理图标,就会显示你导入了哪几个版本的LVGL,根据需要去选择即可
第三步:
16、32或64位微控制器或处理器
推荐16 MHz时钟速度
闪存/ROM:> 64 kB(基本要求),> 180 kB(推荐)
RAM:
静态RAM使用率:~2 kB,具体取决于使用的功能和对象类型
堆栈:> 2kB(基本要求),> 8 kB(推荐)
动态数据(堆):> 2 KB(基本要求),> 16 kB(推荐)
显示缓冲区:> 水平分辨率像素(基本要求),> 10 × 水平分辨率(推荐)
一个帧缓冲区在MCU或外部显示控制器中
C99或更新的编译器
基本的C(或C++)知识
确认单片机满足以上要求就可以开始愉快的导入了,显示部分点击图中两个选项,这样LVGL导入完成了
导入成功之后会显示以下文件,这种移植的方法就少了一系列繁琐的添加文件操作,直接可以开启驱动移植
第四步:
导入文件之后,开始驱动移植
在进行移植之前,你首先需要有屏幕的驱动程序,有画点函数即可,确定你的屏幕参数:屏幕像素点,屏幕类型。
首先先将 lv_conf_cmsis.h 这个文件打开 ,#if 0改为#if 1启用当前文件参与编译
在 lv_port_disp_template.c 文件当中修改屏幕参数
下面是文件内官方提供的刷屏接口示例,只需要在其中提供自己的屏幕画点函数的驱动
/*Flush the content of the internal buffer the specific area on the display.
*`px_map` contains the rendered image as raw pixel map and it should be copied to `area` on the display.
*You can use DMA or any hardware acceleration to do this operation in the background but
*'lv_display_flush_ready()' has to be called when it's finished.*/
static void disp_flush(lv_display_t * disp_drv, const lv_area_t * area, uint8_t * px_map)
{
if(disp_flush_enabled) {
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int32_t x;
int32_t y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/*Put a pixel to the display. For example:*/
/*put_px(x, y, *px_map)*/
px_map++;
}
}
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_display_flush_ready(disp_drv);
}
增加类似disp_flush的函数,改变最后一个入参,因为这个屏幕支持16位传输,所以入参需要修改为16bit。增加函数声明和函数定义!!!。
在my_disp_flush内增加代码 TFT_Draw_Point(x,y,*px_map); <- 即画点函数
//刷屏函数
static void my_disp_flush(lv_display_t * disp_drv, const lv_area_t * area, uint16_t * px_map)
{
if(disp_flush_enabled) {
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int32_t x;
int32_t y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/*Put a pixel to the display. For example:*/
/*put_px(x, y, *px_map)*/
//px_map++;
TFT_Draw_Point(x,y,*px_map);
px_map++;
}
}
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_display_flush_ready(disp_drv);
}
接着根据需求选择刷屏的方式
- 单缓冲区(Partial Rendering with One Buffer):
- 非全屏双缓冲区(Partial Rendering with Two Buffers):
- 全屏双缓冲区(Double Buffering with Two Full Screen Buffers):
根据现有资源选择方式,下面是利用SRAM来加速刷屏,建立整个屏幕的缓冲区,将这个缓冲区放入SRAM当中,如果硬件支持也可不放在SRAM中,因为我这边使用的屏幕像素点较高,硬件条件不支持这么大的缓冲区,可以将缓冲区放入SRAM中,这种方式,屏幕刷新特别快,且画面没有撕裂感,不会出现画面分裂。比较推荐我一下这种方式继续屏幕缓冲!!!
//偏移 首先先定义数组大小 然后在块的首地址上偏移屏幕的尺寸
static lv_color_t color_buf[MY_DISP_HOR_RES * MY_DISP_VER_RES] __attribute__((at(0x60000000)));
void lvgl_disp_init(void)
{
/* 创建显示实例 */
lv_display_t * disp = lv_display_create(MY_DISP_HOR_RES, MY_DISP_VER_RES);
/* 设置刷新回调函数 */
lv_display_set_flush_cb(disp, my_disp_flush);
/* 设置双缓冲,每个缓冲区大小为DISP_BUF_SIZE,渲染模式为部分渲染 */
//lv_display_set_buffers(disp, buf_1, buf_2, DISP_BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL);
// static lv_color_t buf_1_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
// lv_display_set_buffers(disp, buf_1_1, NULL, sizeof(buf_1_1), LV_DISPLAY_RENDER_MODE_PARTIAL);
lv_display_set_buffers(disp, color_buf, NULL, sizeof(color_buf), LV_DISPLAY_RENDER_MODE_PARTIAL);
//lv_display_set_buffers(disp, buf_3_1, buf_3_2, DISP_BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL);
//lv_display_set_buffers(disp, buf_3_1, buf_3_2, sizeof(buf_3_1), LV_DISPLAY_RENDER_MODE_DIRECT);
}
到这一步,LVGL的配置方面已经结束,接下来增加lvgl心跳函数。
这边比较推荐搭配操作系统使用LVGL,因为:
- 资源管理:操作系统可以有效地管理有限的硬件资源,如内存、CPU时间等。LVGL作为一个图形库,当运行在资源受限的嵌入式设备上时,操作系统可以帮助合理分配和管理这些资源,确保系统的稳定性和响应速度。
- 多任务处理:操作系统支持多任务处理,这对于图形界面尤其重要,因为它可以同时处理用户界面的渲染和用户的输入事件,以及其他后台任务,如网络通信或数据处理。
- 优先级控制:操作系统允许设置任务优先级,这意味着关键的UI任务可以被赋予更高的优先级,确保用户界面的流畅性和交互的即时性。
- 外部设备管理:操作系统可以管理各种外部设备,如触摸屏、键盘等,这对于LVGL来说非常重要,因为它需要与这些输入设备进行交互以提供完整的用户体验。
- 扩展性和维护性:操作系统提供了一个统一的接口和框架,使得LVGL可以更容易地扩展新功能,同时也便于维护和升级。
- 生态系统和兼容性:许多操作系统已经与LVGL进行了适配,提供了相应的库和驱动程序,这使得开发者可以更容易地在不同的硬件平台上部署LVGL应用。
- 性能优化:操作系统可以提供调度策略和优化机制,以提高LVGL的渲染性能和响应速度,尤其是在动画和复杂图形效果方面。
- 错误处理和稳定性:操作系统能够提供错误处理机制,增强系统的稳定性和可靠性,这对于图形界面尤为重要,因为它直接影响到用户体验。
假如学习LVGL时候不会操作系统,其实也没关系,也能通过裸机来实现GUI,将心跳函数启用一个定时器,1MS调用心跳函数,利用时间片的方式实现函数运行,将LVGL里面的lv_task_handler(); 每隔1~10MS调用即可。
首先打开FreeRTOS的tick钩子函数vApplicationTickHook
生成配置,打开文件 freertos.c 添加 lv_tick_inc(1); 心跳函数
void vApplicationTickHook( void )
{
/* This function will be called by each tick interrupt if
configUSE_TICK_HOOK is set to 1 in FreeRTOSConfig.h. User code can be
added here, but the tick hook is called from an interrupt context, so
code must not attempt to block, and only the interrupt safe FreeRTOS API
functions can be used (those that end in FromISR()). */
lv_tick_inc(1); //lvgl heart beat
}
接着在LVGL任务当中添加事件处理函数
在以上图片中看到任务当中有两条函数
osMutexAcquire(LVGL_MutexHandle, osWaitForever);
osMutexRelease(LVGL_MutexHandle);
其实这是互斥锁,为什么呢?不加这所谓的互斥锁能否运行?
在多任务操作系统中,互斥锁(Mutex)是一种同步机制,用于保护共享资源不被多个任务同时访问,从而避免数据不一致或竞态条件。在LVGL任务中加入互斥锁主要是基于以下原因:
- 保护共享资源:在多任务环境中,如果有多个任务可能会同时访问和修改LVGL的内部数据结构或硬件资源(如显示缓冲区),互斥锁可以确保在任何时刻只有一个任务能够执行这些操作。
- 防止数据竞争:没有互斥锁,可能会发生数据竞争,即两个或多个任务同时读写同一数据,导致不可预测的结果和系统不稳定。
- 保证渲染一致性:在LVGL中,渲染操作需要原子性,即在渲染一个帧的过程中不应该被其他任务中断,否则可能会导致显示内容的撕裂或错误。
- 线程安全:LVGL本身不是线程安全的,因此需要外部机制(如互斥锁)来保证在多任务环境中的线程安全。
如果不使用互斥锁,理论上LVGL任务也是可以运行的,但这样做存在风险:
因此,即使在某些情况下系统可能看起来没有立即崩溃,不使用互斥锁也是不推荐的做法。在设计多任务系统时,正确使用同步机制是非常重要的,它有助于提高系统的稳定性和可靠性。在实际应用中,应该总是考虑使用互斥锁或其他同步机制来保护对共享资源的访问。
至此LVGL移植完毕,可以根据LVGL学习来实现优雅的界面咯~
移植可能出现的BUG
已经移植好了步骤,如果显示还会报错
Error: L6406E: No space in execution regions with .ANY selector matchinglv_mem_core_builtin.o(.bss).
Error: L6407E: Sections of aggregate size 0x20000 bytes could not fit into .ANY selector(s).
这是因为LVGL默认的预留大小超过单片机RAM的空间,需要根据硬件去调整,我这边使用了外部的SRAM来拓展单片机的性能,后续编写如果内存不够,也可考虑使用外部SRAM来拓展性能
如果还有报错,就是操作系统分配LVGL的线程不够,增加空间即可
接着修改单片机的堆栈大小,也可以在启动文件里面修改
至此编译无误可以学习LVGL的编写了
运行视频
作者:Hui_studio