?轉載自慕容流年 https://me.csdn.net/qq_41868901
?
1. LVGL簡介
LittlevGL是一個免費的開源圖形庫,提供了創建嵌入式GUI所需的一切,具有易于使用的圖形元素、漂亮的視覺效果和低內存占用。使用效果可以去:LittlevGL開源GUI看看,使用效果真的很是驚艷,這里使用群友的一張圖來看看近年來各種GUI圖形庫的發展趨勢:
2. 演示效果
對于「ESP32」上使用「LVGL」,我在三種設備上進行了測試,屏幕驅動型號分別為:「1.14寸ST7789V」、「3.5寸ILI9488」、「3.5寸ST7796S」,為了方便對比,我在他們上邊創建了同樣的儀表控件,下面請看演示效果:
「我自己的小手表」我在前幾個月基于ESP32制作的小手表,集成多種功能,硬件資源有:「1.14寸屏幕」、「自動下載電路」、「溫濕度」、「RTC時鐘」、「大氣壓計」、「光亮度傳感」、「加速度計」、「撥輪按鍵」、「TFT卡」、「蜂鳴器」、「振動馬達」、「WS2812RGB燈」、「鋰電池管理」,可以說是可以滿足目前的開發要求了;「項目地址 :」 「ESP32-Watch」
「啟明云端GUI開發板」該開發板是「啟明云端」公司推出的一款基于ESP32的GUI開發板,搭載了「3.5寸ST7796S屏幕」和「FT6336U電容觸摸」,開發平臺為自研的「8ms創新視界」拖拽式平臺,有點類似「Mixly」吧,具體使用效果請查看:ESP32GUI開發板
屏幕和觸摸的驅動,我也進行了移植和修改,下面放出修改后的庫:
==①「3.5寸ST7796S屏幕驅動」==? ? ? ? ? ? ? ? ? ? ? ? ? ??? ==②「FT6336U電容觸摸驅動」==
移植了LVGL儀表盤的演示效果:「3.5寸ILI9488屏幕」這種屏幕相信某寶能隨處買到了,屏幕硬件為:「3.5寸ILI9488屏幕」和「XPT2046電阻觸摸」,屏幕分辨率為480x320,在初始化觸摸屏時,也要注意觸摸分辨率也要初始化為480x320,并設置好觸摸方向,下面看演示效果:
看完這些演示,你應該明白我為什么使用LVGL了吧,同樣的GUI源碼可以很方便的在各種移植了LVGL的硬件上使用,達到同樣的演示效果,這是我最喜歡的part了
3. 開始移植
3.1 源碼準備
3.1.1 目前「lvgl」在GitHub上已經有了Arduino上的庫支持,庫名字為 「lv_arduino」進去后你可以發現該庫已經有很多版本了,可以根據自己的喜好下載,并放入到你的「Arduino」的「Library」文件夾下:
「lv_arduino V2.1.5」 版本對應的 「LVGL」 版本為V6,適合已經習慣 「LittlevGL V6」 的老師傅們了。
「lv_arduino V3.0.1」 版本對應的 「LVGL」 版本為V7,適合正在學習的新人們,但是無法兼容老版本的GUI程序。
3.1.2 完成以上操作,你可以根據你選擇的 「lv_arduino」 ?版本下載 「LVGL」 的例程進行學習,版本對應關系請看上述“1和2”,例程鏈接為:「lv_examples」
3.1.3 ?「lv_arduino」 給出的 examples 使用的是 ?「TFT_eSPI」 庫,這個庫刷屏速度比其他庫要快,配合 lv_arduino 的例程可以直接使用。
3.2 學習文檔
- LVGL V7.3.1開發文檔
- LVGL V7.1.0開發文檔
- LVGL V6.1.2開發文檔
當然,嫌麻煩的,可以把文檔下載下來,進去之后頁面頂部有PDF下載按鈕。但是,這種英文文檔對我們來說著實不友好,尤其是我這種英語四級還沒通過的zhazha,還記得我之前提過GUI代碼是通用的嘛,因此你可以學習其他廠商的LVGL教程,畢竟GUI代碼通用,只是接口移植部分不一樣而已。
- 正點原子手把手教你學littleVGL【輕量級開源GUI】
- 微雪電子 STM32之littlevGL系列教程
3.3 移植
==由于屏幕眾多,就不一一演示移植過程了,下面就以最簡單的 「3.5寸 ILI9488」屏幕為例,「lv_arduino」 庫版本為 「lv_arduino V3.0.1」 ,購買鏈接就不放出來了,大家通過圖片在某寶搜索即可,尺寸隨意,但是我感覺3.5寸顯示更漂亮:==屏幕的引腳說明為:
【屏幕驅動】:「TFT_eSPI」
【觸摸驅動】:「TFT_Touch」
【LVGL庫】:「lv_arduino」
3.3.1 ?第1步(下載庫)
將 「TFT_eSPI」 、「TFT_Touch」、「lv_arduino」 三個庫放入到你的「Arduino」的「Library」文件夾下。
3.3.2 ?第2步(修改屏幕驅動庫TFT_eSPI)
打開「Arduino」的「Library」文件夾下的「TFT_eSPI」目錄,打開 「User_Setup_Select.h」 文件:
將第22行的
#include????????????//?Default?setup?is?root?library?folder`??
注釋掉為
//#include????????????//?Default?setup?is?root?library?folder`??
將第47行的
//#include????????????//?Setup?file?for?ESP32?and?ILI9488?SPI?bus?TFT`??
取消注釋為
#include????????????//?Setup?file?for?ESP32?and?ILI9488?SPI?bus?TFT`??
完成以上操作后打開 「User_Setups」 目錄下的 「Setup21_ILI9488.h」 文件:
將其內容刪掉并修改為:
//?See?SetupX_Template.h?for?all?options?available
#define?ILI9488_DRIVER
//#define?TFT_INVERSION_OFF
#define?TFT_MISO?19?//?(leave?TFT?SDO?disconnected?if?other?SPI?devices?share?MISO)
#define?TFT_MOSI?23
#define?TFT_SCLK?18
#define?TFT_CS????15??//?Chip?select?control?pin
#define?TFT_DC????2??//?Data?Command?control?pin
#define?TFT_RST???4??//?Reset?pin?(could?connect?to?RST?pin)
#define?TFT_BL????21?//背光引腳
#define?LOAD_GLCD???//?Font?1.?Original?Adafruit?8?pixel?font?needs?~1820?bytes?in?FLASH
#define?LOAD_FONT2??//?Font?2.?Small?16?pixel?high?font,?needs?~3534?bytes?in?FLASH,?96?characters
#define?LOAD_FONT4??//?Font?4.?Medium?26?pixel?high?font,?needs?~5848?bytes?in?FLASH,?96?characters
#define?LOAD_FONT6??//?Font?6.?Large?48?pixel?font,?needs?~2666?bytes?in?FLASH,?only?characters?1234567890:-.apm
#define?LOAD_FONT7??//?Font?7.?7?segment?48?pixel?font,?needs?~2438?bytes?in?FLASH,?only?characters?1234567890:.
#define?LOAD_FONT8??//?Font?8.?Large?75?pixel?font?needs?~3256?bytes?in?FLASH,?only?characters?1234567890:-.
#define?LOAD_GFXFF??//?FreeFonts.?Include?access?to?the?48?Adafruit_GFX?free?fonts?FF1?to?FF48?and?custom?fonts
#define?SMOOTH_FONT
//?#define?SPI_FREQUENCY??20000000
//?#define?SPI_FREQUENCY??27000000
//?#define?SPI_FREQUENCY??40000000
//?#define?SPI_FREQUENCY??80000000
#define?SPI_FREQUENCY??60000000
//?Optional?reduced?SPI?frequency?for?reading?TFT
#define?SPI_READ_FREQUENCY??16000000
#define?SPI_TOUCH_FREQUENCY??2500000
3.3.3 ?第3步(ESP32與屏幕接線)
屏幕引腳 | ESP32引腳 |
---|---|
VCC | 5V |
GND | GND |
CS | IO15 |
RESET | IO4 |
DC/RS | IO2 |
SDI(MOSI) | IO23 |
SCK | IO18 |
LED | IO21 |
SDO(MISO) | IO19 |
—— | —— |
T_CLK | IO25 |
T_CS | IO26 |
T_DIN | IO27 |
T_DO | IO14 |
T_IRQ | 不接 |
3.3.4 第4步 (測試例程)
/**
*?@name:liuzewen
*?@title:lvgl_test
*?@time:2020/8/14
*/
#include?
#include?
#include?
#include?
#include?
#define?LVGL_TICK_PERIOD?60
TFT_eSPI?tft?=?TFT_eSPI();?/*?TFT?instance?*/
static?lv_disp_buf_t?disp_buf;
static?lv_color_t?buf[LV_HOR_RES_MAX?*?10];
lv_obj_t?*?slider_label;
int?screenWidth?=?480;
int?screenHeight?=?320;
//觸摸
#define?DOUT?14??/*?Data?out?pin?(T_DO)?of?touch?screen?*/
#define?DIN??27??/*?Data?in?pin?(T_DIN)?of?touch?screen?*/
#define?DCS??26??/*?Chip?select?pin?(T_CS)?of?touch?screen?*/
#define?DCLK?25??/*?Clock?pin?(T_CLK)?of?touch?screen?*/
TFT_Touch?touch?=?TFT_Touch(DCS,?DCLK,?DIN,?DOUT);
int?X_Raw?=?0,?Y_Raw?=?0;
#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);
??delay(100);
}
#endif
/*?Display?flushing?*/
void?my_disp_flush(lv_disp_drv_t?*disp,?const?lv_area_t?*area,?lv_color_t?*color_p){
??uint16_t?c;
??tft.startWrite();?/*?Start?new?TFT?transaction?*/
??tft.setAddrWindow(area->x1,?area->y1,?(area->x2?-?area->x1?+?1),?(area->y2?-?area->y1?+?1));?/*?set?the?working?window?*/
??for?(int?y?=?area->y1;?y?<=?area->y2;?y++)?{
????for?(int?x?=?area->x1;?x?<=?area->x2;?x++)?{
??????c?=?color_p->full;
??????tft.writeColor(c,?1);
??????color_p++;
????}
??}
??tft.endWrite();?/*?terminate?TFT?transaction?*/
??lv_disp_flush_ready(disp);?/*?tell?lvgl?that?flushing?is?done?*/
}
bool?my_touchpad_read(lv_indev_drv_t?*?indev_driver,?lv_indev_data_t?*?data){
????uint16_t?touchX,?touchY;
????bool?touched?=?touch.Pressed();//檢測觸摸是否按下
????//獲取屏幕坐標
????touchX?=?touch.X();
????touchY?=?touch.Y();
????if(!touched)
????{
??????return?false;
????}
????if(touchX>screenWidth?||?touchY?>?screenHeight)
????{
??????Serial.println("Y?or?y?outside?of?expected?parameters..");
??????Serial.print("y:");
??????Serial.print(touchX);
??????Serial.print("?x:");
??????Serial.print(touchY);
????}
????else
????{
??????data->state?=?touched???LV_INDEV_STATE_PR?:?LV_INDEV_STATE_REL;?
??
??????/*Save?the?state?and?save?the?pressed?coordinate*/
??????//if(data->state?==?LV_INDEV_STATE_PR)?touchpad_get_xy(&last_x,?&last_y);
?????
??????/*Set?the?coordinates?(if?released?use?the?last?pressed?coordinates)*/
??????data->point.x?=?touchX;
??????data->point.y?=?touchY;
??
??????Serial.print("Data?x");
??????Serial.println(touchX);
??????
??????Serial.print("Data?y");
??????Serial.println(touchY);
????}
????return?false;?/*Return?`false`?because?we?are?not?buffering?and?no?more?data?to?read*/
}
lv_obj_t?*?gauge1;
long?date?=?0;
uint8_t?date_cotter?=?0;
void?setup()?{
??//屏幕背光采用PWM調光
??ledcSetup(10,?5000/*freq*/,?10?/*resolution*/);
??ledcAttachPin(TFT_BL,?10);
??analogReadResolution(10);
??ledcWrite(10,1023);
??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);
??//觸摸初始化
??touch.setCal(481,?3395,?755,?3487,?480,?320,?1);
??//旋轉
??touch.setRotation(3);
??lv_disp_buf_init(&disp_buf,?buf,?NULL,?LV_HOR_RES_MAX?*?10);
??//顯示刷新接口
??lv_disp_drv_t?disp_drv;
??lv_disp_drv_init(&disp_drv);
??disp_drv.hor_res?=?screenWidth;
??disp_drv.ver_res?=?screenHeight;
??disp_drv.flush_cb?=?my_disp_flush;
??disp_drv.buffer?=?&disp_buf;
??lv_disp_drv_register(&disp_drv);
??//觸摸板輸入接口
??lv_indev_drv_t?indev_drv;
??lv_indev_drv_init(&indev_drv);?????????????/*Descriptor?of?a?input?device?driver*/
??indev_drv.type?=?LV_INDEV_TYPE_POINTER;????/*Touch?pad?is?a?pointer-like?device*/
??indev_drv.read_cb?=?my_touchpad_read;??????/*Set?your?driver?function*/
??lv_indev_drv_register(&indev_drv);?????????/*Finally?register?the?driver*/
???//放置儀表盤控件
????static?lv_color_t?needle_colors[1];
????needle_colors[0]?=?LV_COLOR_PURPLE;
????//gauge控件
????gauge1?=?lv_gauge_create(lv_scr_act(),?NULL);
????lv_gauge_set_needle_count(gauge1,?1,?needle_colors);
????lv_obj_set_size(gauge1,?300,?300);
????lv_obj_align(gauge1,?NULL,?LV_ALIGN_CENTER,?0,?0);
????//設置儀表指針指向的值
????lv_gauge_set_value(gauge1,?0,?0);
????date?=?millis();
}
void?loop()?{
??lv_task_handler();?/*?let?the?GUI?do?its?work?*/
??delay(5);
??if(millis()-date>20)
??{
????date?=?millis();
????date_cotter++;
????date_cotter?=?date_cotter>100?0:date_cotter;
????//刷新儀表指針指向的值
????lv_gauge_set_value(gauge1,?0,?date_cotter);
????Serial.printf("date_cotter:?%d\n",date_cotter);
??}
}
上傳程序到你的ESP32開發板,即可看到屏幕上出現儀表控件,并且指針也在轉圈圈,哈哈!
3.3.5 第5步 (跑一下官方demo)
最后在啟明云端的板子和3.5寸的ILI9488屏幕上跑一下LVGL官方的demo程序lv_demo_widgets()
,下面放出演示效果:
3.3.6 第6步 (注意事項)
==如果你按照過程操作,卻未看到預想的效果,請注意重新認真看一遍本文檔,并留意其中每一句話。==
?轉載自慕容流年 https://me.csdn.net/qq_41868901
?
