Zephyr上ssd1306协同lvgl工作

Creative Commons
本作品采用知识共享署名

本文介绍如何在zephyr上配置ssd1306和lvgl,并让二者协同工作.

LittlevGL(lvgl) 是一个开源免费的GUI, zephyr将其作为gui组件。SSD1306是一款单色的OLED屏,只有0.96寸大,分辨率为128*64。 一般情况下我们都是直接对SSD1306进行控制,在其framebuffer上画图需要的东西。但在Zephyr上,我们可用通过lvgl很方便在SSD1306上显示出我需要的内容。本文描述如何在Zephyr上配置ssd1306和lvgl,并基于nrf52832演示lvgl和ssd1306协同工作。

配置SSD1306

ssd1306一般有SPI和I2C接口,这里使用的I2C接口,配置SSD1306很简单只用在设备树中描述SSD1306使用的I2C情况,并在配置文件中启动SSD1306既可

设备树修改

在你板子的dts文件中加入如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
&i2c0 {
status = "okay";
sda-pin = <5>;
scl-pin = <4>;
clock-frequency = <I2C_BITRATE_FAST>;

ssd1306@3c{
compatible = "solomon,ssd1306fb";
reg = <0x3c>;
label = "SSD1306";
width = <128>;
height = <64>;
segment-offset = <0>;
page-offset = <0>;
display-offset = <0>;
segment-remap;
com-invdir;
prechargep = <0x22>;
};
};

以上内容含义如下:

  • ssd1306使用i2c0,SDA为引脚5, SCK为引脚4
  • I2C通讯速率为400kbps(参考nclude/dt-bindings/i2c/i2c.h)
  • ssd1306的i2c地址为3c
  • ssd1306的lable为SSD1306 (zephyr display drv name,后面配置LVGL display name要用到)
    其它ssd1306的参数可以参考dts/bindings/display/solomon,ssd1306fb.yaml,都会被对应的转化为宏在驱动代码ssd1306.c里面体现,这里就不做详细介绍。

配置文件修改

详见注释

1
2
3
4
5
6
7
8
9
# 启用I2C驱动
CONFIG_I2C_NRFX=y
CONFIG_I2C_0=y
CONFIG_I2C=y
# 启用SSD1306驱动
CONFIG_SSD1306=y

# 不兼容SH1106,后面专门解释
CONFIG_SSD1306_SH1106_COMPATIBLE=n

LVGL

zephyr已经针对不同的显示形式用自己的display drv对接了LVGL的显示驱动接口,所以对于LVGL来说基本就是配置好就可以直接使用了

配置

基础配置

要想ssd1306能在lvgl下工作,需要启动lvgl并针对ssd1306的显示特性进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 启用display drv
CONFIG_DISPLAY=y

# 启用LVGL
CONFIG_LVGL=y

# 根据SSD1306的特性配置 LVGL
CONFIG_LVGL_COLOR_DEPTH_1=y
CONFIG_LVGL_BITS_PER_PIXEL=1
CONFIG_LVGL_HOR_RES=128
CONFIG_LVGL_VER_RES=64
CONFIG_LVGL_THEMES=y
CONFIG_LVGL_THEME_MONO=y
CONFIG_LVGL_DPI=30
CONFIG_LVGL_VDB_SIZE=100

#指定LVGL用的display drv name,和前文dts中ssd1306 lable对应
CONFIG_LVGL_DISPLAY_DEV_NAME="SSD1306"

组件配置

为了最小化image,zephyr默认是关闭所有lvgl组件的,因此要根据需求启用组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#启用lable
CONFIG_LVGL_OBJ_LABEL=y
CONFIG_LVGL_OBJ_TEXT_AREA=y

#启动button
CONFIG_LVGL_OBJ_CONTAINER=y
CONFIG_LVGL_OBJ_BUTTON=y

#启用bar
CONFIG_LVGL_OBJ_BAR=y
CONFIG_LVGL_ANIMATION=y

#启用UTF8,显示汉字用
CONFIG_LVGL_TXT_ENC_UTF8=y

使用

lvgl的使用不是本文重点,因此这里只列一下测试代码。
在zephyr中一旦配置ssd1306和lvgl,对应的初始化代码就会被打开,不需要手动再写,只用进行主题初始化就可以使用lvgl组件了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//ssd1306是单色屏,因此只能初始化为mono
lv_theme_mono_init(0 /* hue */,NULL /* use LV_FONT_DEFAULT */);
lv_theme_set_current( lv_theme_get_mono() );

//lable使用汉字
LV_FONT_DECLARE(hang)
static lv_style_t style1;
lv_style_copy(&style1, &lv_style_plain);
style1.text.font = &hang; /*Set the base font whcih is concatenated with the others*/

title_label = lv_label_create(lv_scr_act(), NULL);
lv_obj_set_pos(title_label, 40, 0);
lv_label_set_style(title_label, LV_LABEL_STYLE_MAIN, &style1);
lv_label_set_text(title_label, "下载中");

//lable使用英文
hello_world_label = lv_label_create(lv_scr_act(), NULL);
lv_obj_set_pos(hello_world_label, 3, 27);
lv_label_set_text(hello_world_label, "pc:");

//两个按键
lv_obj_t * btn_pause = lv_btn_create(lv_scr_act(), NULL);
lv_obj_set_pos(btn_pause, 4, 48);
lv_obj_set_size(btn_pause, 46, 12);
lv_obj_t * label_pause = lv_label_create(btn_pause, NULL);
lv_label_set_text(label_pause, "pause");

lv_obj_t * btn_cancel = lv_btn_create(lv_scr_act(), NULL);
lv_obj_set_pos(btn_cancel, 80, 48);
lv_obj_set_size(btn_cancel, 46, 12);
lv_obj_t * label_cancel = lv_label_create(btn_cancel, NULL);
lv_label_set_text(label_cancel, "exit");

//一个bar
lv_obj_t * bar1 = lv_bar_create(lv_scr_act(), NULL);
lv_obj_set_pos(bar1, 26, 25);
lv_obj_set_size(bar1, 80, 12);

//一个变化的lable
count_label = lv_label_create(lv_scr_act(), NULL);
lv_obj_set_pos(count_label, 110, 27);

//将屏显打开
display_dev = device_get_binding(CONFIG_LVGL_DISPLAY_DEV_NAME);
display_blanking_off(display_dev);

//更新bar和变化的lable
while (1) {
if ((count % 100) == 0U) {
if(count/100<100){
sprintf(count_str, "%d", count/100U);
lv_label_set_text(count_label, count_str);
lv_bar_set_value(bar1, count/100U, LV_ANIM_OFF);
}
}
lv_task_handler();
k_sleep(2);
++count;
}

以上显示的结果如下
lvgl

其它

驱动

目前zephyr master的code按照前面配置会工作异常,是因为上ssd1306的驱动支援并不完整,只能进行全屏更新,无法对指定显示区域进行更新,因此要进行修改后才能支援lvgl, 详见ssd1306驱动要点最后一个小节, 我已提交PR: https://github.com/zephyrproject-rtos/zephyr/pull/18817, merge后就可以生效了。由于没有SH1106无法对其测试,因此该修改只针对SSD1306有效,这就是为什么要在前面将关闭CONFIG_SSD1306_SH1106_COMPATIBLE的原因。

lvgl的porting

在lvgl外设驱动上zephyr目前只对display和fs进行了对接,porting代码见lib/gui/lvgl
ssd1306单色屏的display porting文件是lib/gui/lvgl/lvgl_display_mono.c