[STM32U5] 【NUCLEO-U5A5ZJ-Q测评】无需移植使用lvgl驱动SSD1306 OLED显示中英文和绘图

[复制链接]
 楼主| HonestQiao 发表于 2023-12-10 13:16 | 显示全部楼层 |阅读模式
<
本帖最后由 HonestQiao 于 2023-12-10 13:22 编辑

这篇文章分享的内容,是在Zephyr系统中,直接使用lvgl驱动 NUCLEO-U5A5ZJ-Q 连接的SSD1306 OLED显示屏,来显示中英文字符,以及绘图。


一、硬件连接
NUCLEO-U5A5ZJ-Q开发板提供了Arduino兼容接口,我手头有之前在Arduino Uno R3上使用的SSD1306 OLED,IIC接口的,可以应用上来。
查看手册,可以得知Arduino兼容接口部分IIC接口的位置:
iShot_2023-12-10_13.17.23.png

在开发板上的实际位置如下:
iShot_2023-12-10_13.18.21.png

然后,将IIC接口的SSD 1306 OLED连接到Arduino兼容引脚:
44781702179685_.pic.jpg

一定要注意你所使用的SSD1306 OLED的驱动电压是5V还是3.3V, 不然可能会烧毁。

二、驱动SSD1306 OLED
经过研究Zephyr的文档,发现Zephyr已经内置了LVGL模块,并且还能够用于驱动SSD1306。
下面就是一个简单的实例代码,用于在屏幕上显示Hello World,并且还有一个计数器计数:
  1. #include <zephyr/device.h>
  2. #include <zephyr/devicetree.h>
  3. #include <zephyr/drivers/display.h>
  4. #include <zephyr/drivers/gpio.h>
  5. #include <lvgl.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <zephyr/kernel.h>

  9. #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
  10. #include <zephyr/logging/log.h>
  11. LOG_MODULE_REGISTER(app);

  12. static uint32_t count;

  13. int main(void)
  14. {
  15.     char count_str[11] = {0};
  16.     const struct device *display_dev;
  17.     lv_obj_t *hello_world_label;
  18.     lv_obj_t *count_label;

  19.     display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
  20.     if (!device_is_ready(display_dev)) {
  21.         LOG_ERR("Device not ready, aborting test");
  22.         return 0;
  23.     }

  24. <blockquote>hello_world_label = lv_label_create(lv_scr_act());


上述的代码逻辑较为简单,简单说明如下:
首先是引入lvgl头文件:
  1. #include <lvgl.h>


然后再定义一个计数器:
  1. static uint32_t count;


在main调用中,先检查设备是否初始化好了:
  1. display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
  2.     if (!device_is_ready(display_dev)) {
  3.         LOG_ERR("Device not ready, aborting test");
  4.         return 0;
  5.     }


再创建2个Lable对象用于显示文字和计数器,并给按键设置好回调。
  1. <blockquote>hello_world_label = lv_label_create(lv_scr_act());


最后,在while循环中,更新计数器的值:
  1.     while (1) {
  2.         if ((count % 100) == 0U) {
  3.             sprintf(count_str, "%d", count/100U);
  4.             lv_label_set_text(count_label, count_str);
  5.         }
  6.         lv_task_handler();
  7.         ++count;
  8.         k_sleep(K_MSEC(10));
  9.     }


需要注意的是,在Zephyr中,没有处理LVGL的自动刷新,需要自己调用 lv_task_handler() 来更新显示内容到屏幕上。


要在工程中使用LVGL模块,还需要在prj.conf中添加下面的配置:
  1. CONFIG_LV_Z_MEM_POOL_SIZE=16384
  2. CONFIG_LV_Z_SHELL=y
  3. CONFIG_MAIN_STACK_SIZE=2048

  4. CONFIG_DISPLAY=y
  5. CONFIG_DISPLAY_LOG_LEVEL_ERR=y

  6. CONFIG_LOG=y
  7. CONFIG_SHELL=y

  8. CONFIG_LVGL=y
  9. CONFIG_LV_MEM_CUSTOM=y
  10. CONFIG_LV_USE_LOG=y
  11. CONFIG_LV_USE_LABEL=y
  12. CONFIG_LV_USE_BTN=y
  13. CONFIG_LV_USE_ARC=y
  14. CONFIG_LV_USE_IMG=y
  15. CONFIG_LV_USE_MONKEY=y
  16. CONFIG_LV_FONT_MONTSERRAT_14=y


编译的时候,需要使用下面的参数:
  1. west build -b nucleo_u5a5zj_q . -- -DSHIELD=ssd1306_128x64


编译烧录后,运行结果如下:
44791702181884_.pic.jpg

我所使用的SSD 1306 OLED为底色上黄下蓝,所以看到是有颜色的,但实际显示的时候,就是底色能使上面的颜色,显示文本只有黑色一种。

三、图形的绘制
使用LVGL,还可以在SSD1306 OLED上面绘图。
不过需要注意的是,毕竟SSD1306分辨率有限,显示颜色只有单色,所以效果一般,但用于一些基础信息的显示,还是可以的。
下面就演示在SSD1306 OLED上面,根据给定的坐标点,画一条折线:
  1. lv_obj_clean(lv_scr_act());
  2.     static lv_point_t line_points[] = { {5, 15}, {30, 50}, {60, 20}, {90, 60}, {120, 30} };

  3.     /*Create style*/
  4.     static lv_style_t style_line;
  5.     lv_style_init(&style_line);
  6.     lv_style_set_line_width(&style_line, 1);
  7.     // lv_style_set_line_color(&style_line, lv_palette_main(LV_PALETTE_WHITE));
  8.     lv_style_set_line_rounded(&style_line, true);

  9.     /*Create a line and apply the new style*/
  10.     lv_obj_t * line1;
  11.     line1 = lv_line_create(lv_scr_act());
  12.     lv_line_set_points(line1, line_points, 5);     /*Set the points*/
  13.     lv_obj_add_style(line1, &style_line, 0);
  14.     lv_obj_center(line1);

  15.         lv_task_handler();


在上述代码中,先定义了一个一批坐标到 line_points,然后定义划线的样式 style_line,再使用 lv_line_create 创建 Line对象,使用lv_line_set_points添加要绘制的处理的折线点,以及使用 lv_obj_add_style 设置样式。

最终的显示效果如下:
44801702182485_.pic.jpg

四、中文的显示
LVGL对Unicode字符的处理提供了很好的处理,包括中文在内。
不过,要显示中文,需要提前准备好专门的字形文件。
我们可以直接把一个ttf字体,转换为字形文件,但是这样包含的中文字符比较多,生成的文件比较大,嵌入式设备不一定都能存放得下,而且运行起来速度也会收到影响。
所以,通常情况下,只会为需要显示的中文字符,生成对应的字形文件。
要给LVGL生成中文字形文件,可以使用lv_font_conv,可以从 https://github.com/lvgl/lv_font_conv 下载。

下面,我要在显示屏上显示"你好,世界!",那么可以试用如下的指令来生成:
  1. lv_font_conv --no-compress --format lvgl --font OPPOSans-M.ttf -o opposans_m_16_2.c --bpp 4 --size 16 -r 0x20-0x7F --symbols 你好,世界!
上述指令的含义,是从 OPPOSans-M.ttf 字体文件中,提取 "你好,世界!" 的字形数据以及ASCII码对应的字形数据,生成16号字体对应的c文件opposans_m_16_2.c,生成后的内容如下:
iShot_2023-12-10_13.21.01.png

因为字形数据较多,所以只展示上面一部分。

将生成的字形数据的c文件,放置到项目的src目录下,然后,在代码中,include 之后,添加字体定义:
  1. LV_FONT_DECLARE(opposans_m_16_2);

然后设置一个演示使用中文字体:
  1. static lv_style_t label_style_cn;                                                                                                // 创建一个风格
  2. lv_style_init(&label_style_cn);                                                                                                // 初始化风格
  3. lv_style_set_text_font(&label_style_cn, &opposans_m_16_2);        // 设置字体


再给前面显示的 hello_world_label 添加定义的样式:
  1. lv_label_set_text(hello_world_label, "Hello world!");
  2. lv_obj_align(hello_world_label, LV_ALIGN_CENTER, 0, 0);
  3. lv_obj_add_style(hello_world_label, &label_style_cn, LV_PART_MAIN);                // 应用效果风格


最后,在代码中调用:
  1. while (1) {
  2.         if ((count % 100) == 0U) {
  3.                 sprintf(count_str, "%d", count/100U);
  4.                 lv_label_set_text(count_label, count_str);

  5.                 if (((count / 100) % 2) == 0U) {
  6.                         lv_label_set_text(hello_world_label, "Hello world!");
  7.                 } else {
  8.                         lv_label_set_text(hello_world_label, "你好,世界!");
  9.                 }
  10.         }
  11.         lv_task_handler();
  12.         ++count;
  13.         k_sleep(K_MSEC(10));
  14. }


编译运行后,实际效果如下:
44811702183114_.pic.jpg


五、总结
NUCLEO-U5A5ZJ-Q的良好硬件设计,使得开发者可以很方便的连接Arduino兼容的硬件,大大方便了开发工作。
Zephyr对NUCLEO-U5A5ZJ-Q的给力支持,以及提供的LVGL模块支持,能够让开发者聚焦于核心业务的处理,真的是非常的方便。
有一点需要注意的是,Zephyr提供的LVGL版本并不是最新的,所以有极少部分调用,可能与新版本的LVGL存在差异,编译的时候会有提示。
后续将会继续使用彩色显示屏来进行测试,并继续给大家分享。
xusiwei1236 发表于 2023-12-11 10:14 | 显示全部楼层
帮主牛X
AdaMaYun 发表于 2023-12-13 22:51 | 显示全部楼层
Zephyr提供的LVGL版本之间的差异有哪些?
szt1993 发表于 2023-12-13 22:56 | 显示全部楼层
LVGL移植需要注意哪些方面呢
 楼主| HonestQiao 发表于 2023-12-15 17:15 | 显示全部楼层
AdaMaYun 发表于 2023-12-13 22:51
Zephyr提供的LVGL版本之间的差异有哪些?

和官方的差异很小,实例我都是拿来直接使用的
您需要登录后才可以回帖 登录 | 注册

本版积分规则

42

主题

110

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部