项目功能介绍
该项目主要使用基于RP2040的LVGL图形化控制终端完成温度设计。通过温湿度传感器可以实时的将温度和湿度以数字的形式显示到屏幕上,同时还使用LVGL为温度计设计了一款UI,将其模拟成老式温度计的效果。
设计思路
这个温度计的设计可以拆分成两个小的任务,并行进行,最后再将两个任务进行整合联调。一个任务是驱动LCD屏幕进行显示,第二个任务是驱动温度传感器进行测量温度。
开发平台的选择
在进行温度计的设计之前,首先应该进行开发平台的选择。这款主控芯片是RP2040,其主要支持三种开发平台,分别是Arudino、MicroPyhton以及官方的C-SDK开发模式,其实还有RT-Thread平台也支持RP2040的开发,但使用人数相较于前三种较少。以上四种平台我均尝试过使用软件SPI驱动显示LCD,但使用RTT平台时,遇到屏幕刷新卡死,可能是由于自己操作不当导致。首先自己对Python掌握没有C语言熟练,其次在MicroPyhon环境移植LVGL需要自己进行编译,这部分操作还没弄懂因此放弃了使用MicroPyhon;使用C-SDK开发时,一大优点就是性能发挥到极致,这点从我使用软件SPI刷新屏幕的速度便能看出,但是在使用这种开发方式时需要使用者对CMake语法的掌握程度有较高的要求,因此放弃使用C-SDK开发方式。最后Arduino使用C/C++语言开发,同时开发者也不需要编写复杂的编译脚本,只需做少许的设置便能使用,因此在本次开发中使用Arduino平台进行开发。
图形化显示任务
LVGL对接硬件主要有三大端口,分别是显示端口,输入端口,文件系统端口。本项目中未使用到输入端口和文件系统端口,因此只需对接好显示端口即可。
LCD驱动
在拿到一块屏幕时,第一步一定是先把他点亮,以确保硬件没有损坏,再进行后续的实验开发,这样后续出现bug时可以优先排除硬件问题节约时间。点亮一块SPI屏幕最简单最便捷的就是使用软件SPI驱动。因此本项目先使用软件SPI进行测试无误后再使用硬件SPI提高屏幕刷新速度。我这里驱动屏幕使用的是TFT_eSPI库进行驱动,原因之一是这个库对各种屏幕的适配度高,LVGL的官方demo里也是使用的这个库。第二个原因是该库对RP2040芯片也做了适配,可以很方便的应用到本项目上。
LVGL的移植
LVGL目前可以直接从Arduino的LIBRAY MANAGER也就是库管理中进行搜索下载,但是下载时要选择好合适的版本,v9版本太新了一些教程还没有适配,出现bug可能自己无法解决,同时我所使用的GUI设计器所支持的版本为8.3.5,因此此次开发,图形库使用LVGL 8.3.5版本。
GUI设计器的移植
对于一些好看的UI界面,组件一个一个手动编写费时又费力,因此选择一款合适GUI设计器来简化操作十分重要。目前对LVGL适配较好的主要有2款设计器,一款是NXP公司开发的GUI_Guider,一款时LVGL官方开发Squareline Studio。Squareline Studio需要收费,不付费使用会有一些限制,因此本次项目开发,GUI设计器使用GUI_Guider。
测量温度任务
测量温度目前常用的有DHT系列和SHT系列,虽然DHT11价格上比SHT30便宜,但是其精度远不及SHT30,DHT11 的温度测量精度为 ±2°C,SHT30的温度测量精度为 ±0.3°C,本次项目属于个人DIY,因此不考虑设计成本,主要考虑设计效果,所以温度测量选用SHT30传感器。
硬件框图
硬件框图主要关注蓝色方框,查看LCD对应连接的引脚号,以及IIC驱动的SHT30对应连接的引脚号。
开发板原理:
温度传感器原理图:
软件流程图
简单的硬件介绍
- 主控芯片使用RP2040,RP2040 是一款由 Raspberry Pi 设计的低成本、高性能的微控制器芯片。它采用双核 Arm Cortex-M0+ 处理器架构,主频高达 133 MHz,内置 264KB 的 SRAM。RP2040 还具有丰富的外设和接口,包括多个 GPIO 引脚、SPI、I2C、UART、PWM 等,以及专用的 PIO(Programmable I/O)引擎,可用于实现高速并行数据处理。此外,RP2040 还支持 USB 1.1 主机和设备功能。
- 屏幕的分辨率为240x240,使用的驱动芯片是ST7789,ST7789 是一款常见的彩色 TFT LCD 控制器芯片。它支持显示分辨率为 240x320 的图形和文本,并具有丰富的功能和接口选项。ST7789 芯片采用 SPI 接口进行通信,可以与微控制器或其他主控设备连接。它具有内置的显示控制器和图形加速器,能够快速渲染图像和文本,实现平滑的动画效果。
- 温湿度测量模块使用的SHT30模块,SHT30 模块是一种常用的数字式温湿度传感器模块,采用 Sensirion 公司的 SHT30 传感器芯片。它可以测量环境的温度和湿度,并通过数字接口向主控设备提供准确的温湿度数据。SHT30 模块具有高精度和稳定性,能够在广泛的工作温度范围内提供可靠的测量结果。它采用数字式输出,通过 I2C接口与主控设备通信。SHT30 模块的特点包括快速响应时间、低功耗、高线性性能和低漂移率。它还具有内置的温度补偿和湿度补偿算法,可以提供更精确的测量结果。
实现的功能及图片展示
使用基于RP2040的LVGL图形化控制终端实现了温度计功能,可以精准的实现温度与湿度的读取。同时可以对应改变屏幕中的模拟温度计动画。
主要代码片段说明
移植TFT屏幕显示驱动
驱动屏幕,我这里使用的是TFT_eSPI库,可以使用Arduino的库管理进行下载,
他的使用方式主要关注如下文件
首先打开 User_Setup.h
,打开对应驱动,配置屏幕大小,同时要注意,驱动只能选择一个,不使用的驱动要记得注释掉。
然后打开 User_Setup_Select.h
文件,选择对应的驱动文件,这里咱么的屏幕驱动芯片使用的是ST7789且分辨率为240x240,因此我选择 Setup24_ST7789.h
作为驱动文件,这里要注意一点,只能选择一个文件,要注意不要同时开两个注释,否则会造成不正常显示。
最后,打开 User_Setups
文件夹,找到刚刚选择的对应头文件 Setup24_ST7789.h
,然后打开该文件,对引脚进行设置,这里根据自己的原理图进行修改
至此,TFT_eSPI的驱动便移植完成,可以打开Arduino打开一个demo进行测试,如果测试无误,便可以进行下一阶段的调试,即LVGL的移植。
移植LVGL
LVGL同样可以从Arduino的库管理中进行下载,我这里选择8.3.5版本,是为了和gui设计器版本对应,大家可以自行选择。
安装完成后,可以对内置示例进行编译,会报如下错误
这个错误的原因在于没有找到 lv_conf.h
文件,解决方法:可以打开lvgl所在的文件夹,将lv_conf_template.h
文件重命名为 lv_conf.h
在重命名完成后,打开文件,然后将代码中的 #if 0
修改为 #if 1
但是仅修改这里还是会报错,原因在于 src
文件夹中的 lv_conf_internal.h
文件,路径包含不对,根据文件结构,应该将 lv_conf_internal.h
中的代码进行修改,即找到 #include "../../lv_conf.h"
这行代码,将其修改为 #include "../lv_conf.h"
。
接下来,在这个文件里,使用搜索文档功能搜索 LV_TICK_CUSTOM
,将 #define LV_TICK_CUSTOM 0
改为 #define LV_TICK_CUSTOM 1
,否则LVGL会卡在第一帧而不再进行刷新。
然后进行编译,此时,应该还会报错,但是已经不是这个错误,而是tft屏幕触摸的错误,由于咱们没有使用到触摸,因此可以注释掉相关代码
将这些代码进行注释
同时把这个函数也注释掉
然后再进行编译,会报关于demo的错误,这个不影响咱们开发,可以暂时忽略。
解决方法,把demo头文件包含注释掉,然后将下列代码的 #if 0
改为 #if 1
然后再次进行编译,会提示编译成功!!!
但是先不要着急上传,还需要进行最后一步修改,将代码中的屏幕宽度和高度设置为适合自己屏幕的大小。
/*Change to your screen resolution*/
static const uint16_t screenWidth = 240;
static const uint16_t screenHeight = 240;
然后进行上传,屏幕上会出现一行字,即代表lvgl移植成功。
移植GUI_Guider生成的代码
首先应该将GUI_Guider生成的.c和.h文件都移动到和.ino文件同一文件夹下,否则Arduino不会对其进行编译。
我这里编写了一个bat脚本来对其进行一键移动,bat脚本仅供参考。
@echo off
REM 获取当前批处理文件的目录
set "SCRIPT_DIR=%~dp0"
REM 移动 guider_customer_fonts 文件夹中的文件
move /Y "%SCRIPT_DIR%generated\guider_customer_fonts\*" "%SCRIPT_DIR%"
REM 移动 guider_fonts 文件夹中的文件
move /Y "%SCRIPT_DIR%generated\guider_fonts\*" "%SCRIPT_DIR%"
REM 移动 images 文件夹中的文件
move /Y "%SCRIPT_DIR%generated\images\*" "%SCRIPT_DIR%"
REM 移动 generated 文件夹中的文件
move /Y "%SCRIPT_DIR%generated\*.c" "%SCRIPT_DIR%"
move /Y "%SCRIPT_DIR%generated\*.h" "%SCRIPT_DIR%"
REM 移动 custom 文件夹中的文件
move /Y "%SCRIPT_DIR%custom\*.c" "%SCRIPT_DIR%"
move /Y "%SCRIPT_DIR%custom\*.h" "%SCRIPT_DIR%"
echo 文件移动完成。
#pause
然后在Arduino的ino文件中添加如下代码,包含对应的头文件,定义一个全局变量 `lv_ui guider_ui;`
#define USE_GUI_GUIDER 1
#if USE_GUI_GUIDER
#include "custom.h"
#include "events_init.h"
#include "gui_guider.h"
lv_ui guider_ui; // gui
#endif
在 `setup()`函数中添加如下代码,注意这段代码要添加在lvgl初始化之后,
#if USE_GUI_GUIDER
setup_ui(&guider_ui);
events_init(&guider_ui);
custom_init(&guider_ui);
#endif
测量温度
测量温度使用的是SHT30传感器,我同样使用Arudino中的库进行开发,使用其 periodicmode.ino
进行修改。
添加对应的库
#include <Wire.h>
#include "ClosedCube_SHT31D.h"
ClosedCube_SHT31D sht3xd;
在 `setup()` 中添加下列代码进行初始化
/* iic温湿度传感器初始化 */
Wire.begin();
delay(100);
sht3xd.begin(0x44); // I2C address: 0x44 or 0x45
if (sht3xd.periodicStart(SHT3XD_REPEATABILITY_HIGH, SHT3XD_FREQUENCY_10HZ) != SHT3XD_NO_ERROR)
Serial.println("[ERROR] Cannot start periodic mode");
Serial.println("Setup done");
将获取到的温湿度结果显示到屏幕上
void Show_Result_T_H(SHT31D result) {
if (result.error == SHT3XD_NO_ERROR) {
char temp_string[] = "00.00";
char humi_string[] = "00.00";
if(result.t <= 50)
sprintf(temp_string, "%.2f", result.t); // 格式化温湿度数据字符串
else
result.t = 50;
sprintf(humi_string, "%.2f", result.rh);
lv_label_set_text(guider_ui.screen_1_temp, temp_string);
lv_label_set_text(guider_ui.screen_1_humi, humi_string);
lv_bar_set_value(guider_ui.screen_1_bar_1, result.t + 40, LV_ANIM_OFF);
} else {
Serial.print(": [ERROR] Code #");
Serial.println(result.error);
}
}
遇到的主要难题及解决办法
移植GUI_Guider生成的图片c文件时,报错
报错效果如下
出现这个问题的原因在于代码路径指向不对,需要进行修改生成的代码,
有两种修改方式,任选一种均可,一是添加宏定义
#define LV_LVGL_H_INCLUDE_SIMPLE
二是将包含路径进行修改,将 #include "lvgl/lvgl.h"
修改为 #include "lvgl.h"
。
未来的计划或者建议
本次项目为温度计设计,没有使用到板载的按键,造成了资源的部分浪费,后续计划学习lvgl的输入设备,将板载的按键运用到lvgl的控制当中。