基于arduino的ESP32 学习笔记(二) TFT_eSPI和LVGL库使用笔记

前言

本文的目的是为了给将要制作的ESP32手环做技术储备

记录基于arduino的ESP32驱动TFT-LCD屏幕的配置过程,并且进一步使用LVGL这个GUI框架

硬件准备

  • ST7789 240x240 1.3寸 SPI接口LCD屏 16位色深RGB565

  • ESP-WROOM-32E开发板

  • ESP32的数据手册链接 https://download.csdn.net/download/weixin_42487906/21119656

软件准备

  • Arduino 1.8.9

相关链接

LVGL移植到arduino的官方文档 https://docs.lvgl.io/7.11/get-started/arduino.html#get-the-lvgl-ardunio-library

TFT_eSPI github仓库 https://github.com/Bodmer/TFT_eSPI

相关博客 https://blog.csdn.net/weixin_44007534/article/details/113653031?spm=1001.2014.3001.5501

LVGL移植到arduino的官方文档里提到LVGL在arduino运行是基于TFT_eSPI库,所以我们先配置TFT_eSPI库

TFT_eSPI库配置

在库管理器下载TFT_eSPI库

安装好后不能直接用,需要针对自己使用的LCD来完成配置

安装好的库通常在 C:\Users\Alpha\Documents\Arduino\libraries Alpha是我的用户名

进入TFT_eSPI库文件夹,打开User_Setup_select.h

在这里插入图片描述

注释掉User_Setup.h头文件,同时找到适合自己屏幕的头文件,解开他的注释

image-20210817155132689

最终效果如下

//#include <User_Setup.h>           // Default setup is root library folder
#include <User_Setups/Setup24_ST7789.h>            // Setup file configured for ST7789 240 x 240

然后还要进到Setup24_ST7789.h,完成引脚更改。根据开发板的数据手册,MOSI设置为IO23引脚,也就是VSPID,SCLK设置为IO18引脚,也就是VSPICLK,其他脚接到一般的IO上就行,记得RST引脚一定记得接到板子的引脚上不能省,LCD的BLK背光控制引脚可以不用接悬空。剩下一些字库的配置暂时不管
在这里插入图片描述

完成硬件上的接线,除了MOSI,SCLK,DC,RST外还要接VCC,GND总共6条线

然后在tft_espi的示例代码中找到boing_ball示例程序,别的示例程序也可以,来做个测试,这个程序虽然是320*240的但是也能在我们的屏幕上用,编译下载观察现象

在这里插入图片描述

可以看到屏幕正常显示

LVGL库配置

在库管理器中安装lvgl和lv_examples,保险起见我安装的是7.6.0版本

在这里插入图片描述

下面根据,LVGL移植到arduino的官方文档完成配置

步骤一

下图,官方文档让我们进到库所在文件夹里,把lv_conf_template.h从lvgl文件夹里拿出来放到与lvgl同级的文件夹里,并改名为lv_conf.h

在这里插入图片描述

在这里插入图片描述

最终的效果如下图

打开lv_conf.h,将LV_TICK_CUSTOM设为1

在这里插入图片描述

把分辨率和色深改成自己屏幕的,将最上面的#if 0改成#if 1

在这里插入图片描述

步骤二

将lv_examples文件夹中lv_ex_conf_template.h拿出来放到与lv_exmaples文件夹同级的文件夹中,也就是libraries文件夹中,并改名为lv_ex_conf.h

在这里插入图片描述

在这里插入图片描述

最终效果如下图

在这里插入图片描述

修改 lv_ex_conf.h 中内容,将#if 0 改为 #if 1

在这里插入图片描述

根据官方文档,找到LVGL_Arduino.ino,打开这个工程

在这里插入图片描述

打开工程后,提示建立同名文件夹,建立后重启arduino就可以看到lvgl的示例了

示例工程LVGL_Arduino.ino中也有包含触摸屏的部分,本文用不到,就将其删去

注意要加入#include <lv_examples.h>,不然示例程序会报错,示例程序是以函数的形式出现的lv_ex_btn_1();

还有最后的loop中

void loop()
{
    long last_time = millis();
    lv_task_handler(); /* let the GUI do its work */
    delay(5);
    lv_tick_inc(int(millis()-last_time));
}

原本是这样的,但是参考一些博客后,按照上面代码方式改,可以保证lvgl的获得的执行时间是准确的,理论上lv_tick_inc()执行的间隔实践会决定LVGL刷屏的最大帧率

下面是工程的所有代码

void loop()
{
    lv_task_handler(); /* let the GUI do its work */
    delay(5);
    lv_tick_inc(5);
}
#include <lvgl.h>
#include <lv_examples.h>
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI(); /* TFT instance */
static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];

#if USE_LV_LOG != 0
/* Serial debugging */
void my_print(lv_log_level_t level, const char * file, uint32_t line, const char * dsc)
{

    Serial.printf("%s@%d->%s\r\n", file, line, dsc);
    Serial.flush();
}
#endif

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.pushColors(&color_p->full, w * h, true);
    tft.endWrite();

    lv_disp_flush_ready(disp);
}



void setup()
{
    Serial.begin(115200); /* prepare for possible serial debug */

    lv_init();

#if USE_LV_LOG != 0
    lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif

    tft.begin(); /* TFT init */
    tft.setRotation(1); /* Landscape orientation */


    lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);

    /*Initialize the display*/
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 240;
    disp_drv.ver_res = 240;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.buffer = &disp_buf;
    lv_disp_drv_register(&disp_drv);

    

		/* Try an example from the lv_examples repository
		 * https://github.com/lvgl/lv_examples */
		lv_ex_btn_1();
}


void loop()
{
    long last_time = millis();
    lv_task_handler(); /* let the GUI do its work */
    delay(5);
    lv_tick_inc(int(millis()-last_time));
}
  • 12
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
很高兴为您提供帮助! 下面是一个简单的示例代码,您需要将其修改为适合您的硬件和网络设置的版本。 ``` #include <WiFi.h> #include <WiFiClientSecure.h> #include <HTTPClient.h> #include <ArduinoJson.h> #include <TFT_eSPI.h> #include <lvgl.h> // 修改为您的 WiFi SSID 和密码 const char* ssid = "your_SSID"; const char* password = "your_PASSWORD"; // 修改为您在心知天气注册的 API Key 和城市 ID const char* apiKey = "your_API_KEY"; const char* cityId = "your_CITY_ID"; // 修改为您的 TFT_eSPI 硬件设置 TFT_eSPI tft = TFT_eSPI(); #define TFT_WIDTH 240 #define TFT_HEIGHT 240 void setup() { Serial.begin(115200); // 初始化 TFT_eSPI 硬件 tft.begin(); tft.setRotation(1); tft.fillScreen(TFT_BLACK); // 连接 WiFi Serial.printf("Connecting to WiFi %s...\n", ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.print("."); } Serial.println(""); Serial.printf("WiFi connected, IP address: %s\n", WiFi.localIP().toString().c_str()); // 初始化 LVGL lv_init(); lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); disp_drv.hor_res = TFT_WIDTH; disp_drv.ver_res = TFT_HEIGHT; disp_drv.flush_cb = [](lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p) { tft.startWrite(); tft.setAddrWindow(area->x1, area->y1, area->x2, area->y2); uint32_t size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1); tft.pushColors((uint16_t*)color_p, size, true); tft.endWrite(); lv_disp_flush_ready(disp_drv); }; lv_disp_drv_register(&disp_drv); lv_theme_t* theme = lv_theme_material_init(210, NULL); lv_theme_set_current(theme); // 创建 LVGL 控件 lv_obj_t* label_time = lv_label_create(lv_scr_act(), NULL); lv_obj_align(label_time, NULL, LV_ALIGN_CENTER, 0, -50); lv_label_set_text(label_time, "Loading..."); lv_obj_t* label_date = lv_label_create(lv_scr_act(), NULL); lv_obj_align(label_date, NULL, LV_ALIGN_CENTER, 0, 50); lv_label_set_text(label_date, "Loading..."); // 更新天气和时间 updateWeather(); updateTime(); } void loop() { // 每分钟更新一次时间和天气 static uint32_t lastUpdateTime = 0; if (millis() - lastUpdateTime >= 60000) { updateWeather(); updateTime(); lastUpdateTime = millis(); } // 处理 LVGL 事件 lv_task_handler(); delay(5); } void updateWeather() { // 发送 HTTP 请求获取天气数据 WiFiClientSecure client; if (!client.connect("api.seniverse.com", 443)) { Serial.println("Failed to connect to weather server"); return; } String url = "/v3/weather/now.json?key=" + String(apiKey) + "&location=" + String(cityId); client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: api.seniverse.com\r\n" + "User-Agent: ESP32\r\n" + "Connection: close\r\n\r\n"); unsigned long timeout = millis(); while (client.available() == 0) { if (millis() - timeout > 5000) { Serial.println("Failed to receive weather data"); return; } delay(100); } // 解析 JSON 数据 String line = client.readStringUntil('\n'); while (!line.startsWith("{")) { line = client.readStringUntil('\n'); } DynamicJsonDocument doc(1024); DeserializationError error = deserializeJson(doc, line); if (error) { Serial.println("Failed to parse weather data"); return; } JsonObject weather = doc["results"][0]["now"]; const char* text = weather["text"]; int temperature = weather["temperature"]; // 更新 LVGL 控件 lv_obj_t* label_weather = lv_label_create(lv_scr_act(), NULL); lv_obj_align(label_weather, NULL, LV_ALIGN_CENTER, 0, 100); lv_label_set_text_fmt(label_weather, "%s %d°C", text, temperature); } void updateTime() { // 获取当前时间 time_t now = time(nullptr); struct tm* timeinfo = localtime(&now); // 更新 LVGL 控件 lv_obj_t* label_time = lv_scr_act()->child_ll; lv_label_set_text_fmt(label_time, "%02d:%02d", timeinfo->tm_hour, timeinfo->tm_min); lv_obj_t* label_date = label_time->sibling_ll; lv_label_set_text_fmt(label_date, "%04d-%02d-%02d", timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday); } ``` 请注意,此示例代码需要您在心知天气注册并获取 API Key 和城市 ID。此外,您需要安装 TFT_eSPILVGL ,并将其与您的硬件设置匹配。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值