lvgl 双物理显示器的驱动实现
2023-12-26 07:39:09
目录
一、背景
1. 要实现的功能
一个mcu 物理连接两块彩屏,使用一个lvgl内核实现对双显示器的显示驱动。
2. lvgl 版本
V8.3.x
二、简单粗暴的方式
理论上可以这样实现
- 软件应用层将两款显示器虚拟成一整块显示器。显示器的宽度就是最宽那个显示器的宽度,显示的长度是显示器1 + 显示器2 的长度。
- 在刷屏打点函数中(
disp_flush
),根据坐标进行判断,属于显示器1 范围的数据,写到物理显示器1;属于显示器2范围的数据,写到物理显示器2。
缺陷:
- 可能存在边界数据处理异常问题;
- 对lvgl 来说,屏幕宽度和高度是虚拟的。如果想实现参考屏幕本身对齐的功能,比较难实现。
- 在软件编码实现UI时,需要将显示坐标转换成虚拟坐标。不够直观。例如要实现分别在两块显示器的(0,0) 坐标开始位置写数据。写显示器1时,可以写(0,0) 坐标;写显示2时,需要写(0,
LCD_SCR1_Y_MAX
);
三、lvgl 自身机制支持
3.1 实现思路
- 为每一个显示器分配并初始化一个缓冲区;
- 为每一个显示器注册显示驱动;
- 每次更新显示内容前,先选中当前的显示器
3.2 初始化缓冲区和注册显示驱动
3.2.1 复制lv_port_disp → lv_port_disp_2
显示缓冲区初始化和显示器注册的处理部分都在 lv_port_disp.c
文件中;复制一份 lv_port_disp.c
(lv_port_disp.h
)文件,改名为 lv_port_disp_2.c
( lv_port_disp_2.h
)负责显示器2的初始化。
3.2.2 修改 lv_port_disp_2 文件
- 更正显示器2的x,y 坐标范围
- 将每一个可能因此重定义的变量或函数重命名。例如
- lv_port_disp_init → lv_port_disp_init_2
3.2.3 在应用层调用显示器2初始化程序
在调用 lv_port_disp_init()
后面,调用 lv_port_disp_init_2()
3.3 如何切换显示器
3.3.1 切换显示器的 API
完成上面步骤之后,就已经完成了显示器缓冲区分配,显示驱动注册。
通过阅读官方帮助文档,我们知道 lv_disp_set_default(disp)
用来切换当前的默认显示器。
void lv_disp_set_default(lv_disp_t * disp)
{
disp_def = disp;
}
参数 disp
就是要设置的显示器指针。
3.3.2 如何找到显示器指针
大致看了一遍相关的源码文件,没看到直接返回显示器1,显示器2的相关接口函数。重新研究了一遍注册显示器的过程,发现 lv_disp_drv_register
注册显示驱动函数,会返回当前注册的显示器指针。这个函数在lv_port_disp.c
文件中调用。
由于默认只有一个显示器,所以默认也没接收这个函数的返回值,记录为当前显示器指针。
lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver)
3.3.3 接收显示器指针
以显示器1 为例说明。以下代码都是在 lv_port_disp.c
文件中实现。
定义保存显示器1 指针的变量
static lv_disp_t *_local_lv_disp_1;
接收显示器1 指针
_local_lv_disp_1 = lv_disp_drv_register( &disp_drv );
提供 API 供上层访问
lv_disp_t *lv_port_get_screen_1( void )
{
return _local_lv_disp_1;
}
3.3.4 另外一种返回显示器指针的实现方式
查看lv_disp_drv_register
函数,发现函数的第一行实现如下:
lv_disp_t * disp = _lv_ll_ins_head(&LV_GC_ROOT(_lv_disp_ll));
插入一个 接节点到列表LV_GC_ROOT(_lv_disp_ll)
的头部,并返回新插入的节点的指针。列表 LV_GC_ROOT(_lv_disp_ll)
就是物理显示器列表。
那么,我们遍历整个 LV_GC_ROOT(_lv_disp_ll)
列表,就能依次返回物理显示器的指针。由于应用层先注册显示器1,所以显示器1的指针位于队列尾。
返回显示器1指针
lv_disp_t *lv_port_get_screen_1( void )
{
return _lv_ll_get_tail( &LV_GC_ROOT( _lv_disp_ll ) );
}
返回显示器2 指针
lv_disp_t *lv_port_get_screen_2( void )
{
return _lv_ll_get_head( &LV_GC_ROOT( _lv_disp_ll ) );
}
3.3.5 应用层切换显示器实现
选择显示器1
lv_disp_set_default(lv_port_get_screen_1());
选择显示器2
lv_disp_set_default(lv_port_get_screen_2());
四、参考链接
- LVGL Displays :https://docs.lvgl.io/8.3/overview/display.html
文章来源:https://blog.csdn.net/gdut_liujiangyi/article/details/135206431
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!