【得捷电子Follow me第2期】基于lvgl构建的多任务程序框架(综合贴)
[复制链接]
本帖最后由 genvex 于 2023-10-13 10:04 编辑
项目目录
一、编程环境搭建
二、屏幕驱动及Lvgl移植
三、开机动画设计
四、主要功能展示
五、中文字体处理
六、任务5 手机远程遥控板载led灯
总结:心得体会
前言:
本项目内容使用Feather TFT 及 LIS3DH加速度传感器、AHT20 温湿度传感器 硬件,开发了一套用户UI,包括了时钟、硬件信息、温湿度、天气预报、加速度传感器使用,最后,使用blinker库完成手机远程遥控板载led灯的功能。
一、编程环境搭建
项目是基于Arduino在vscode的PlatformIO开发平台完成的。Arduio用来管理小型单功能项目还是可以的,主要是它编程环境准备相对简单,大众创客的挚爱。进阶的使用可以将战场转移到vscode,利用vscodeIDE提供的自带技能及其他优秀插件带来的便利,来提升编程能力。
(1)新建一个Feather的项目
(2)对项目的配置进行微调
vscode 编程环境就相对复杂一点,同样的内容在别人机器上好好的,在你的电脑上就可能玩不转了,因为很难保证大家的环境细节完全一致,所以调整配置文件也是件技术活,也没啥技巧就是多看别人的配置。如果编译通过但是不能上传,就需要指定 esptoolspy的版本,才能够解决这个问题。FeatherTFT 带有PSRAM在这里作了相关声明,后面才可以正常使用。另外,对flash分区也作了调整,默认情况只有1.多M的空间,把所有项目相关内容都一次放进去还是比较紧张的,这里我调整到3M左右的空间,跑起来就没有顾忌。
二、屏幕驱动及Lvgl移植
成功的嵌入式设备需要一个极具吸引力的用户界面,才能给用户留下良好的第一印象。传统的图形绘制库(例如经典TFT_eSPI,Adafruit_GFX,Lovyan_GFX等)可以帮助用户快速的入门,lvgl的出现给嵌入式开发在最终产品呈现提供了一种解决方案。但是lvgl的版本一直在演替,带来的问题是版本间的差异会给刚入门的小伙伴造成很大的困扰,即便是想运行一个helloworld测试,就要折腾好久,甚至弃坑,都是很正常的现象。
Lvgl的驱动核心驱动代码见下图所示。核心思想是用常规的作图库为lvgl提供一个刷屏的工具函数。我这里用了LovyanGFX来驱动Feather的屏幕,因为这个图形库有专门针对FeatherS3的驱动支持,得来全不费工夫,直接使用就好,到驱动底层内容去观摩了下,作者对屏幕的x,y方向做个偏移设置,就是这两个参数,自己上手就够有一阵折腾的。构建好这个工具函数后,把它传递到lvgl驱动核心,在配置上主要是把屏幕的尺寸、刷屏的缓存大小,触控驱动(本次没用上)的参数,看似长篇大论,但是一个环节也不能少,这个架构也是经过多次模仿学习,本人认为是一个较为优雅的解决方案。这里特别强调一下,这个驱动把刷屏的内存放在PSRAM里面,目前PSRAM最常见使用就是用来刷屏,这样做可以节省些常规内存,保障程序和图形界面的正常运行,这样设置下来,屏幕能保持在50~60fps的刷新率,也是相当流畅的。
- static void lvgl_begin(void)
- {
- #define LVGL_HOR_RES (240)
- #define LVGL_VER_RES (135)
-
- lcd.init();
- lcd.initDMA();
- lcd.setRotation(2);
-
- lv_init();
-
- static lv_disp_draw_buf_t draw_buf;
- size_t DISP_BUF_SIZE = sizeof(lv_color_t) * (LVGL_HOR_RES * LVGL_VER_RES);
- static lv_color_t *buf1 = (lv_color_t *)heap_caps_malloc(
- DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
- static lv_color_t *buf2 = (lv_color_t *)heap_caps_malloc(
- DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
-
-
-
-
-
-
- lv_disp_draw_buf_init(&draw_buf, buf1, buf2, DISP_BUF_SIZE);
-
-
-
-
- static lv_disp_drv_t disp_drv;
- lv_disp_drv_init(&disp_drv);
-
-
-
- disp_drv.hor_res = LVGL_HOR_RES;
- disp_drv.ver_res = LVGL_VER_RES;
-
- disp_drv.flush_cb = my_disp_flush;
-
- disp_drv.draw_buf = &draw_buf;
-
- lv_disp_drv_register(&disp_drv);
- }
-
- static void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area,
- lv_color_t *color_p)
- {
- int w = (area->x2 - area->x1 + 1);
- int h = (area->y2 - area->y1 + 1);
-
- lcd.startWrite();
-
- lcd.setAddrWindow(area->x1, area->y1, w, h);
-
-
-
- lcd.writePixelsDMA((lgfx::rgb565_t *)&color_p->full, w * h);
-
-
-
-
-
- lcd.endWrite();
-
- lv_disp_flush_ready(disp);
- }
-
三、开机动画设计
模仿Epressif demo的开机动画制作了一个开机动画,这个动画的运行机理和Lvgl的动画机制不一样。这个动画使用了一个时间计数器(count)来推动动画的前进,随便变量count的步进,三个大小不一的圆弧产生旋转的效果最后消失。主要脑洞比较大的地方是设计圆弧的起始角度和结束角度,这两个数值决定了圆弧的长度,用三角函数的数值变化区间来实现圆弧的从无到有再缩短,视觉效果就产生旋转效应。为了便于理解对计数器变化引起图形数值的变化做了一幅曲线图,观察起始角度和结束角度的变化曲线,蓝色线条是起始角度在整个周期的数值变化曲线,在开始一段时间(count<0)的阶段保持在0度(即默认的起始位置,这里需要复习一下arc的绘图语法,见代码块),在这个阶段结束角度在慢慢变大(橙色线),呈现了圆弧在伸长。下一阶段(count>0),开始角度(蓝色线)也开始递增,同时,结束角度(橙色线)也继续保持上升状态,这阶段呈现了弧形在旋转的效果,因为开始角度和结束角度同时移动;但由于开始角度增长速度比较快(斜率大),很快就与结束角度重合,这个阶段呈现了弧形在不断地缩短直到最后消失,至此,弧形的动画结束,最后是乐鑫和Adafruit公司的logo的淡入(fadein)和淡出(fadeout),利用的cos函数从第四象限到第一象限数值呈钟形的曲线效果,实现淡入淡出。红色虚线表开始角度和结束角度的距离,也就是弧形的长度变化曲线, 而真正产生旋转效果的参数是 rotation一直在匀速增加,推进了3调弧形的位置。
- lv_arc_set_bg_angles(arc[i], arc_start, arc_end);
-
-
-
-
-
-
-
- lv_arc_set_rotation(arc[i], (count + 120 * (i + 1)) % 360);
同时,板载的led灯也跟随count变量的变化产生渐变,跟动画互相辉映,产生出来灯火绚烂的效果。最后动画结束后用回调函数钩子把主程序带动起来。
四、主要功能展示
本项目的主要功能包括时钟、主板性能测试、传感器数值显示面板(模拟),天气预报,泡泡5个板块。各板块使用相同的类模板进行构造,通过板载按键实现循环切换,理论上可以无限添加,只要flash空间够大。
(1)主程序:
主程序模块
(2)监控模块
用于检测和展示主板的硬件信息。
(3)温湿度监控模块
采集了使用AHT20 温湿度传感器的数据并采用仪表盘的形式展示出来。
- void MonitorModel::init(){
- aht.begin();
- getAlldata();
- }
- void MonitorModel::getAlldata(){
- sensors_event_t humid, temp;
- aht.getEvent(&humid, &temp);
- Serial.print(temp.temperature);
- Serial.print(humid.relative_humidity);
- this->temperature = temp.temperature;
- this->humidity = humid.relative_humidity;
-
- }
- float MonitorModel::getTemperature() {
- return this->temperature;
- }
-
-
- long MonitorModel::getHumidity() {
- return this->humidity;
- }
-
(4)时钟模块
(5)天气模块
(6)加速度传感器及泡泡球屏保
读取了Adafruit_LIS3DH加速度传感器数据并显示在屏幕上。
- void BubbleModel::init()
- {
- if (!lis.begin(0x18))
- {
- Serial.println("Couldnt start");
- while (1)
- yield();
- }
-
- lis.setRange(LIS3DH_RANGE_4_G);
- Serial.print("Range = "); Serial.print(2 << lis.getRange());
- Serial.println("G");
- lis.setDataRate(LIS3DH_DATARATE_50_HZ);
- }
-
-
- void BubbleModel::getAlldata()
- {
- lis.read();
- sensors_event_t event;
- lis.getEvent(&event);
-
- Serial.print("\tX: "); Serial.print(event.acceleration.x);
- Serial.print("\tY: "); Serial.print(event.acceleration.y);
- Serial.print("\tZ: "); Serial.print(event.acceleration.z);
- this->accX = event.acceleration.x;
- this->accY = event.acceleration.y;
- this->accZ = event.acceleration.z;
-
- }
五、中文字体处理
转中文字体虽然没有太多的技术难度,就是有点麻烦例如选什么字体(自己觉得好看,才是真的好看,纠结),字号(要跟屏幕大小匹配,来回要调整几次),字数(要转哪些字,多了文件大,少了不够用,导致某些不常见情况会出现乱码)。在没有其他选择的时候,大家会用lvgl官方提供的在线转化工具,稍微有点懵,用几次也可以掌握。国内民间也有好些转图转字体的好用的工具,本项目采用的就是好心人制作转字体工具LvglFontTools0.4,虽然界面有点丑,但是转出来的字体能正常使用,还要什么单车呢。
教程和参见下方链接和导图(不好用免费退款 。
中文字体制作:
https://blog.csdn.net/kelleo/article/details/122686644
六、任务5 手机远程遥控板载led灯
实现遥控有很多解决方案,例如本地局域网的控制,乐鑫rainmaker方案,mqtt方案。前期测试了rainmaker的方案,没有成功,而且需要flash空间比较大,运行还不太稳定。局域网控制方案,需要设计一个网页,网页元素包括三条滑动条,用来控制灯光的三种颜色,也设计好了,就是过程较为繁琐。最后,选用了比较优雅的方案,使用了点灯科技的blinker库来实现项目要求的功能,该库的背后原理是mqtt机制,但是经过封装后,开发出来给用户使用的api非常简单,另外,还贴心提供手机客户端的app,使用起来更加方便,不足之处是只提供一个硬件的接入,但是算得上良心商家了。
- void rgb1_callback(uint8_t r_value, uint8_t g_value, uint8_t b_value, uint8_t bright_value)
- {
-
- BLINKER_LOG("R value: ", r_value);
- colorR = r_value;
- BLINKER_LOG("G value: ", g_value);
- colorG = g_value;
- BLINKER_LOG("B value: ", b_value);
- colorB = b_value;
- BLINKER_LOG("Rrightness value: ", bright_value);
-
- pixels.setPixelColor(0, r_value, g_value, b_value);
- if (bright_value > 100)
- bright_value = 100;
- brightness = bright_value;
- pixels.setBrightness(brightness);
- pixels.show();
- lv_led_set_brightness(blinker_led, brightness);
- lv_led_set_color(blinker_led, lv_color_make(colorR, colorG, colorB));
- lv_label_set_text_fmt(rlabel, "R:%d", colorR);
- lv_label_set_text_fmt(glabel, "G:%d", colorG);
- lv_label_set_text_fmt(blabel, "B:%d", colorB);
- }
rgb灯的响应回调函数
本项目的一些心得体会:
以往参加活动,针对主办方提出的要求逐一完成任务,把任务分解开来,这样比较轻松的完成单个内容。这次尝试把所有的任务整合在一起,在一次启动就可以展示所有(必做任务)内容,这是一次积极的挑战,通过面向对象的编程思想,把功能用类(class)进行统一的封装,使得不同的功能可以自由切换,而且还能保持流畅运行。理论上只要Flash够大,可以按照类的模板不断的扩展应用程序,相当做个了一个项目的架构,体现了做应用项目的思维,这种思维应该继续使用下去,因为经过简单功能程序学习,最终是需要把这些简单的功能综合起来(综合功能产品,手机),这样才能成为一个有用的工具,一个有趣的产品。
感谢主办方给予我极大的创作空间,祝愿该系列活动完满成功并持续举办下去!
主任务代码 ,主要修改自己wifi信息,高德天气api账号,用到 LIS3DH加速度传感器、AHT20 温湿度传感器 硬件。
附加任务代码 需要使用点灯科技blinker app 及硬件密钥。 预祝食用愉快,有问题,欢迎来聊。 汤半泛!
|