资讯详情

资讯详情

BK7258平台LVGL底层函数替换指南:显示、触摸、内存与时基

1. 先摸清BK7258上LVGL移植层的家底才知道要替换谁做带屏幕的IoT产品尤其用BK7258这类音视频Wi-Fi SoCLVGL基本是默认UI方案。但芯片原厂SDK里给的LVGL永远只能保证“能跑”不能保证“好用”。屏幕型号不对、触控IC不一样、内存分配策略不匹配、甚至动画卡顿都会把问题指向同一个动作替换LVGL的某些底层函数。我接手的项目是拿BK7258做一块带屏设备SDK默认配的是某款SPI屏和一颗电容触控IC。但量产用的屏换了另一家模组触控也换了。第一反应是改设备树和屏初始化结果发现事情没这么简单。原厂SDK把LVGL的显示、触摸、内存、时基全部封装成了默认实现你如果不替换这些函数LVGL跑起来的画面要么黑屏要么触摸乱跳要么动不动内存不足。要替换之前你得先知道替换目标有哪些。BK7258 SDK里的LVGL一般拆成三个层次第一层是LVGL核心库本身的port接口包括lv_port_disp、lv_port_indev、lv_port_fs第二层是SDK自己封装的平台适配层比如Wi-Fi事件分发、音频回调里带上LVGL刷新通知第三层是芯片的内存与时钟管理比如内部SRAM分配、PSRAM的映射方式。真正需要替换的是落到具体驱动上的那几个函数而不是整个库。上手时先别急着改代码先把下面几个东西查清楚SDK自带的LVGL版本号。老版本和新版本的tick接口、draw buffer初始化方式、函数命名都有差异版本不对会导致替换之后编译不过或行为异常。lv_conf.h里的配置项。尤其是LV_MEM_CUSTOM、LV_TICK_CUSTOM、LV_COLOR_DEPTH这几个宏决定了内存、时基是否走自定义实现。原厂默认屏幕驱动文件的初始化函数和flush接口。要确认它用的是GPIO模拟时序还是SPI DMA是RGB565还是RGB888。芯片可用RAM的实际大小。BK7258内部SRAM有限跑起音频和网络协议栈之后留给LVGL的缓存空间可能只剩几十KB。很多替换动作本质上是在跟这几十KB较劲。搞清楚这四项你才有一个清晰的替换清单。在我那个项目里最终替换了四个部分显示驱动的初始化和flush回调、触控驱动读取函数、LVGL内存分配钩子、以及时基函数。下面我把每个部分怎么改、改了之后要验证什么一条条展开说。2. 替换LVGL函数的三种机制本质上是三条不同的路很多人一听到“替换函数”第一反应是把原厂SDK里的.c文件改掉。这确实是一条路但不是唯一的路也不是最稳的路。LVGL的移植层设计得相对开放同一个替换目标至少有三种实现方式我给它们起的名字是回调注册、宏重定义、符号覆盖。你选了哪种后面的维护成本和踩坑概率天差地别。回调注册是LVGL最规范、最推荐的替换方式。显示驱动、输入设备驱动、文件系统驱动都留了函数指针结构体比如lv_disp_drv_t里的flush_cb、lv_indev_drv_t里的read_cb。你只要在注册驱动时把自己的函数地址填进去LVGL就会在需要的时候回调你的实现原厂默认函数根本不会被执行。这种方式的优点是逻辑清晰、功能边界分明什么时候换回来也方便。缺点是你得完整按照LVGL定义的结构体来写回调参数一个都不能错。我最开始替换屏驱动时就把lv_disp_flush_ready写漏了结果画面区域刷新走到一半永远不结束整屏卡成马赛克。宏重定义主要用在内存分配这类地方。LVGL在lv_conf.h里给LV_MEM_CUSTOM、LV_MEM_CUSTOM_INCLUDE、LV_MEM_CUSTOM_ALLOC这些宏预留了开关你只要把宏定义从0改成1再填上自己写的分配函数名整个LVGL内部的lv_mem_alloc就会变成你指定的函数。这种方式改起来非常快捷但对内存管理器的行为要求比较高。比如你替换成芯片SDK自带的堆管理器后如果这个管理器没有对应的realloc实现LVGL里部分依赖realloc的功能就会出问题。我在项目里就遇到过同样的问题后面细说。符号覆盖是最隐蔽的一种方式也最考验链接器的功底。LVGL某些接口比如lv_tick_get是源码里直接定义的普通函数。你要替换它理论上可以在自己的源文件里写一个同名函数靠链接时的强符号规则把它覆盖掉。这个做法在编译驱动上可行但有一个致命前提你自己的函数和LVGL原函数必须在同一编译单元可见性规则下完成覆盖一旦SDK打包成静态库或者编译优化把函数内联了覆盖就会失效。所以做符号覆盖前先确认你的SDK用的是源码编译还是预先编译好的库否则白改一场。三种机制不冲突有时候同一个目标可以混着用。比如我把内存分配用宏重定义切到SDK堆管理器又把tick用符号覆盖的方式换成了自己基于某个硬件定时器的实现。正确理解这三种机制后你才不会被“替换函数”这四个字限制住思路。3. 替换显示驱动与内存管理函数的完整实操显示驱动是替换LVGL函数里最直观、也最容易看出效果的部分。BK7258常见的屏幕接口有两种一种是SPI 并口或DPI接口的RGB屏一种是通过SDK内部的视频接口驱动的MIPI屏。SDK默认的lv_port_disp.c里一般会带一个flush回调里面直接调用原厂屏驱动芯片的写显存函数。换屏之后这个回调基本是废的。我当时把默认驱动文件从头看了一遍发现原厂屏的初始化函数里不仅配置了分辨率还写了不少跟显示效果相关的小参数比如VCOM电压、背光PWM频率、扫描方向。新屏模组如果这些参数不一样直接复用初始化代码会出问题。于是我把显示初始化拆成了三个小函数一个是GPIO和通信总线初始化一个是屏控制寄存器初始化一个是LVGL的draw buffer和lv_disp_drv_register注册。换屏时只改第二个第一个和第三个不动。关键的是flush回调函数。这是一段我后来一直沿用的骨架static void my_disp_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) { // 把color_p指向的区域数据交给屏驱动接口 uint32_t w lv_area_get_width(area); uint32_t h lv_area_get_height(area); uint32_t x_start area-x1; uint32_t y_start area-y1; // 调用SPI DMA发送先设定显示窗口 sdk_lcd_set_window(x_start, y_start, x_start w - 1, y_start h - 1); sdk_lcd_send_pixels((uint8_t *)color_p, w * h * sizeof(lv_color_t)); // 发送完成后必须通知LVGL可以继续调用flush lv_disp_flush_ready(drv); }这里有两个坑。第一个坑是sdk_lcd_send_pixels如果是异步DMA发送你必须在发送完成中断里调用lv_disp_flush_ready而不是在发送函数返回后立刻调用。LVGL在等待flush_ready时会认为当前缓冲区还在被显示引擎占用如果你提前调它就开始往同一个缓冲区里写下一帧造成撕裂如果你不调它就一直等着画面刷新率暴跌。第二个坑是颜色格式。BK7258的屏接口可能是RGB565LVGL里对应的LV_COLOR_DEPTH要设成16否则颜色错乱。如果屏是RGB888务必把LV_COLOR_DEPTH改成32并在flush里按实际位深转换不能直接把lv_color_t指针当字节流发出去。内存管理函数的替换我当时花的时间比显示驱动还多。默认情况下LVGL使用自带的内存分配器lv_mem小型程序够用但在BK7258这种多媒体芯片上音频缓冲、视频帧缓冲、UI缓冲混在一起LVGL如果再用自己的一套堆内存碎片和总量浪费都非常明显。我选择把LVGL的内存分配切到SDK自带的内存管理器。根据系统编译后的map文件SDK内存管理器已经把内部SRAM划分好了几个动态区并且提供了带对齐参数的分配接口。于是在lv_conf.h里做了如下配置#define LV_MEM_CUSTOM 1 #define LV_MEM_CUSTOM_INCLUDE sdk_mem_pool.h #define LV_MEM_CUSTOM_ALLOC sdk_mem_alloc #define LV_MEM_CUSTOM_FREE sdk_mem_free #define LV_MEM_CUSTOM_REALLOC sdk_mem_realloc刚开始我忽略了REALLOC这一项。LVGL内部的文本、列表、容器这几种控件在刷新时经常用realloc来伸缩缓冲区。SDK内存管理器默认提供的sdk_mem_realloc在安全性和碎片整理上会比LVGL自带分配器激进一些但行为模式基本一致。我那时直接把REALLOC留空LVGL在链接阶段报了一堆未定义引用才意识到这三个宏必须配套。替换内存函数后还要测一个东西后台任务里的内存占用有没有变化。我在代码里加了一个周期任务定时读取SDK内存管理器暴露的剩余内存接口和LVGL自己的lv_mem_monitor做对比。实测发现用SDK内存管理器后同一段UI代码的峰值内存占用比LVGL自带堆低了大约15%。代价是分配速度略有下降但这在UI刷新帧率里感知不到。4. 替换时基与输入设备函数细节藏在函数签名和坐标变换里显示和内存搞定后屏幕能亮、UI能动了但会发现两个很别扭的问题动画速度不对触摸位置偏移。这两个问题分别指向时基函数替换和触控驱动替换。LVGL的运行依赖一个ms级的时基。SDK默认实现里LVGL的tick来源可能是某个RTC或者系统节拍定时器。BK7258跑的是带Wi-Fi协议栈的实时系统系统节拍通常给网络任务用了LVGL能拿到的tick更新频率可能忽高忽低。动画时快时慢、滑块跟手性差都跟它有关。我在SDK里找到了一个空闲的通用定时器硬件专门用来给LVGL提供tick。但替换前要注意LVGL版本的函数签名差异。旧版LVGL用的是lv_tick_inc(n)方式要求外部周期调用喂tick新版LVGL改成lv_tick_set_cb让你直接注册一个返回uint32_t毫秒数的函数。我项目里的SDK带的是旧版LVGL核心库又是升级过的两者混用时直接编译不过。最终我选择不再调用SDK里原有的lv_tick_inc代码路径而是自己注册一个读取硬件定时器计数值的函数static uint32_t my_lv_tick_get(void) { return timer_get_ms(); } void my_tick_init(void) { // 如果是新版本用这段 lv_tick_set_cb(my_lv_tick_get); // 如果是旧版本则需要在定时器中断里周期性调用 lv_tick_inc(1) }这段代码看起来简单真正容易忽略的是定时器本身的精度和中断优先级。我初始用的是某个定时器的自由运行模式但它的时钟源选择错误导致返回值不是ms实际偏快了将近一倍。改时钟源分频比之后LVGL动画速度才恢复正常。所以我建议替换tick函数后强制跑一个1秒往返的进度条动画观察它的实际耗时是不是接近1秒这个小验证花不了两分钟能救你半天。触控驱动替换的难点不在I2C读寄存器而在坐标变换和返回状态。BK7258的默认SDK里触控驱动往往只适配特定IC。换新触控IC后需要实现一个read_cb把原始坐标从模组坐标系映射到屏幕的显示坐标系。我当时第一次只改了I2C初始化坐标完全反了原因是新模组的触控扫描方向和屏的扫描方向不一致触摸右上角光标跑到左下角。标准的indev回调长这样static void my_touchpad_read_cb(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) { static lv_coord_t last_x 0; static lv_coord_t last_y 0; if (sdk_touch_get_point(x, y)) { >
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →