ESP32上移植LVGL8.x

目标

配置vscode + platformIO环境,在该环境下为ESP32单片机适配LVGL图像库以及keypad输入设备,驱动液晶显示器。

1.环境配置

1.1安装python解释器

安装[python解释器 这边用的是3.7.1](链接:https://pan.baidu.com/s/1OloK0seytAjSclvIqtui4g
提取码:zdzd ) ,不安装vscode创建platformIO工程可能会报错

安装解释器时勾选上添加变量。安装完成后,将解释器的地址添加到全局变量。

完成后可以在命令行里用pip list指令测试一下,不报错就是安装成功了
在这里插入图片描述

添加系统变量
在这里插入图片描述

测试是不是装成功了
在这里插入图片描述

1.2安装vscode

官网下载百度云提取码:zdzd
直接安装 “下一步 下一步 我接受” ,第一次打开会问是不是安装中文字体再重启点击”是“再打开就中文了。

1.3安装platformIO插件

右下角有安装进度,一般比较慢,经常卡在中间不动。搞个梯子会快很多。如果卡在core很久很久,可以试试关闭防火墙。
在这里插入图片描述

安装的过程中如果提示没有找到python3.6,可以按照指引填写在这儿
在这里插入图片描述

1.4创建工程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dud5XRgI-1651502880941)(images\image-20220430194218683.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KZcJJKzT-1651502880941)(images\image-20220430194911429.png)]

选择完成后点击下面的finish,创建也许会花很长很长时间(可能2天搞不完),因为服务器在外面。用梯子会快很多(我实测1h左右,也许是梯子不是很稳的原因)。如果没梯子可以用手机4G开个热点据说会快很多。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-woVrggO9-1651502880942)(images\image-20220430205316259.png)]

完成找到刚刚保存项目的地址打开文件夹,可以看到src目录下主函数的入口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yPY28IQG-1651502880942)(images\image-20220430210537888.png)]

由于每次新建项目花费的时间比较长,可以将这个空项目备份一下。以后再有项目的时候,直接复制这个空项目,然后在这个空项目上操作。

在main.cpp里写个程序测试一下,插上esp32下载时如果出现upload问题可能是没有安装串口驱动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fxaoXY0A-1651502880942)(images\image-20220430230304496.png)]

2. 显示驱动库的移植

2.1下载TFT_eSPI库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-onpx8Hzl-1651502880942)(images\image-20220501173217376.png)]

在这里插入图片描述

下载完成后可以在这个json文件里看到保存的地址
在这里插入图片描述

根据个人喜欢可以将去剪切到lib文件夹下,目录会自动识别自动修改的
在这里插入图片描述

2.2 修改TFT_eSPI库

打开setup.h文件,选择硬件驱动的型号取消屏蔽
在这里插入图片描述

选择屏幕的宽高,取消屏蔽
在这里插入图片描述

找到修改ESP32 引脚的地方 修改引脚的编号
在这里插入图片描述

2.3 测试TFT_eSPI是否适配成功

复制example文件夹下的例子到main里,看看能不能正常显示,再开头加一个ardunio.h的头文件,编译下载后屏幕会显示彩虹色
在这里插入图片描述

3.安装LVGL库

lvgl是图像库,可以方便的绘制按钮,日历,文本框等图形,也可以方便的接入按钮按下的事件函数,支持css布局。详情可阅读百问网LVGL中文手册。 LVGL大更新的版本并不向低版本兼容,7.X版本与8.X版本的很多接口不一样,需要根据版本阅读相应的手册。

3.1 下载LVGL库

下载LVGL 8.x版本,并且复制粘贴到lib文件夹下,方法同TFT_eSPI

3.2 修改LVGL库适配硬件

复制lv_conf_template.h粘贴到同一个目录下,文件名修改为lv_conf.h
在这里插入图片描述
打开.h文件中的宏 根据实际修改颜色的深度
在这里插入图片描述

3.3 测试一下lvgl库

初始化LVGL 并在屏幕上显示个红色的按钮。 各种接口和组件可以参考手册

#include <Arduino.h>
#include <TFT_eSPI.h>
#include <lvgl.h>


/*Change to your screen resolution*/
static const uint32_t screenWidth  = 160;
static const uint32_t screenHeight = 128;

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

//extern const lv_img_dsc_t picQiudayeda;

#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( ( uint16_t * )&color_p->full, w * h, true );
  tft.endWrite();

  lv_disp_flush_ready(disp);
}


void setup()
{  
    Serial.begin(115200);

    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_draw_buf_init(&disp_buf, buf, NULL, screenWidth * 10);

    /*Initialize the display*/
    static 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.draw_buf = &disp_buf;
    lv_disp_drv_register(&disp_drv);

   
   //画一个按钮
    lv_obj_t *obj =  lv_btn_create(lv_scr_act());

    static lv_style_t style;
    lv_style_set_bg_color( &style, lv_color_hex(0xff0000));

    lv_style_set_width(&style, 60);
    lv_style_set_height(&style, 35);

    lv_obj_add_style(obj, &style, LV_PART_MAIN  );
    lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0 );

}

void loop()
{

  lv_timer_handler(); /* let the GUI do its work */
  // lv_task_handler();
  // lv_tick_inc(5);   有输入设备的时候打开
  delay(5);
}

3.4添加按键(keypad)组件

没有触摸屏的时候,按键组件作为输入可以切换选中按钮等小部件,也可以按下按钮 或者 让进度条的值变大变小。

复制这两个文件到src目录下
在这里插入图片描述

将.c .h文件修改成lv_port_indev

在这里插入图片描述

打开.h文件 修改内容, 打开宏 修改标签 修改lvgl头文件include的地址
在这里插入图片描述

修改.c文件 打开宏 修改include的地址

在这里插入图片描述

.c文件写了四种支持的输入设备的类型,保留想要保留的就行,其余的都删除
在这里插入图片描述

根据注释将整个.c文件中与keypad无关的都删除(只保留想要的输入设备)

lv_port_indev_init 是初始化

keypad_read 是回掉函数 lvgl会自动循环这个函数,判断按键的按下情况

keypad_get_key 按键读取的函数 这个函数在keypad_read里会赋值给act_key变量,表征按键状态的变化

删除之后

/**
 * @file lv_port_indev.c
 *
 */

 /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1

/*********************
 *      INCLUDES
 *********************/
#include "lv_port_indev.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void keypad_init(void);
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static uint32_t keypad_get_key(void);

/**********************
 *  STATIC VARIABLES
 **********************/
lv_indev_t * indev_keypad;


/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_port_indev_init(void)
{
    /**
     * Here you will find example implementation of input devices supported by LittelvGL:
     *  - Touchpad
     *  - Mouse (with cursor support)
     *  - Keypad (supports GUI usage only with key)
     *  - Encoder (supports GUI usage only with: left, right, push)
     *  - Button (external buttons to press points on the screen)
     *
     *  The `..._read()` function are only examples.
     *  You should shape them according to your hardware
     */

    static lv_indev_drv_t indev_drv;

    /*------------------
     * Keypad
     * -----------------*/

    /*Initialize your keypad or keyboard if you have*/
    keypad_init();

    /*Register a keypad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_KEYPAD;
    indev_drv.read_cb = keypad_read;
    indev_keypad = lv_indev_drv_register(&indev_drv);

    /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
     *add objects to the group with `lv_group_add_obj(group, obj)`
     *and assign this input device to group to navigate in it:
     *`lv_indev_set_group(indev_keypad, group);`*/
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
/*------------------
 * Keypad
 * -----------------*/

/*Initialize your keypad*/
static void keypad_init(void)
{
    /*Your code comes here*/
}


static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y)//这个不要删除 在init里调用了 善良
{
    /*Your code comes here*/

    (*x) = 0;
    (*y) = 0;
}

/*Will be called by the library to read the mouse*/
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static uint32_t last_key = 0;

    /*Get the current x and y coordinates*/
    mouse_get_xy(&data->point.x, &data->point.y);

    /*Get whether the a key is pressed and save the pressed key*/
    uint32_t act_key = keypad_get_key();
    if(act_key != 0) {
        data->state = LV_INDEV_STATE_PR;

        /*Translate the keys to LVGL control characters according to your key definitions*/
        switch(act_key) {
        case 1:
            act_key = LV_KEY_NEXT;
            break;
        case 2:
            act_key = LV_KEY_PREV;
            break;
        case 3:
            act_key = LV_KEY_LEFT;
            break;
        case 4:
            act_key = LV_KEY_RIGHT;
            break;
        case 5:
            act_key = LV_KEY_ENTER;
            break;
        }

        last_key = act_key;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }

    data->key = last_key;
}

/*Get the currently being pressed key.  0 if no key is pressed*/
static uint32_t keypad_get_key(void)
{
    /*Your code comes here*/

    return 0;
}


#else /*Enable this file at the top*/

/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif

3.5主函数里初始化keypad

1.在setup里调用 lv_port_indev_init()函数,初始化设备 lv_port_indev_init不在main文件里,故需要在.h里声明

2.在loop里添加 lv_tick_inc(5); 让系统知道5ms到了,没有这个不回掉keypad_read函数的 (这一步很重要)

3.indev_keypad是kepad的句柄 在main函数里需要用所以也需要在.h里声明

4.修改lv_inport_indec.c里的keypad_get_key函数,可以直接在这个里面判断按键是否按下,根据不同的按键return不同的值

也可以在这个返回一个全局变量,按键的判断或者全局变量的改变放在主函数。推荐使用后者耦合性低,方便使用onebutton等按键判断库。
在这里插入图片描述
在这里插入图片描述

src下lv_port_indev.h lv_port_indev.c main.cpp三个文件c代码最后如下

lv_port_indev.h

/**
 * @file lv_port_indev_templ.h
 *
 */

 /*Copy this file as "lv_port_indev.h" and set this value to "1" to enable content*/
#if 1

#ifndef LV_PORT_INDEV_H
#define LV_PORT_INDEV_H

#ifdef __cplusplus
extern "C" {
#endif

/*********************
 *      INCLUDES
 *********************/
#include "lvgl.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 * GLOBAL PROTOTYPES
 **********************/
void lv_port_indev_init(void);
lv_indev_t * indev_keypad;

uint32_t key_state;
/**********************
 *      MACROS
 **********************/

#ifdef __cplusplus
} /*extern "C"*/
#endif

#endif /*LV_PORT_INDEV_TEMPL_H*/

#endif /*Disable/Enable content*/

lv_port_indev.c

/**
 * @file lv_port_indev.c
 *
 */

 /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1

/*********************
 *      INCLUDES
 *********************/
#include "lv_port_indev.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void keypad_init(void);
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static uint32_t keypad_get_key(void);

/**********************
 *  STATIC VARIABLES
 **********************/



/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_port_indev_init(void)
{
    /**
     * Here you will find example implementation of input devices supported by LittelvGL:
     *  - Touchpad
     *  - Mouse (with cursor support)
     *  - Keypad (supports GUI usage only with key)
     *  - Encoder (supports GUI usage only with: left, right, push)
     *  - Button (external buttons to press points on the screen)
     *
     *  The `..._read()` function are only examples.
     *  You should shape them according to your hardware
     */

    static lv_indev_drv_t indev_drv;

    /*------------------
     * Keypad
     * -----------------*/

    /*Initialize your keypad or keyboard if you have*/
    keypad_init();

    /*Register a keypad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_KEYPAD;
    indev_drv.read_cb = keypad_read;
    indev_keypad = lv_indev_drv_register(&indev_drv);

    /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
     *add objects to the group with `lv_group_add_obj(group, obj)`
     *and assign this input device to group to navigate in it:
     *`lv_indev_set_group(indev_keypad, group);`*/
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
/*------------------
 * Keypad
 * -----------------*/

/*Initialize your keypad*/
static void keypad_init(void)
{
    /*Your code comes here*/
}

static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    /*Your code comes here*/

    (*x) = 0;
    (*y) = 0;
}

/*Will be called by the library to read the mouse*/
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static uint32_t last_key = 0;

    /*Get the current x and y coordinates*/
    mouse_get_xy(&data->point.x, &data->point.y);

    /*Get whether the a key is pressed and save the pressed key*/
    uint32_t act_key = keypad_get_key();
    if(act_key != 0) {
        data->state = LV_INDEV_STATE_PR;

        /*Translate the keys to LVGL control characters according to your key definitions*/
        switch(act_key) {
        case 1:
            act_key = LV_KEY_NEXT;
            break;
        case 2:
            act_key = LV_KEY_PREV;
            break;
        case 3:
            act_key = LV_KEY_LEFT;
            break;
        case 4:
            act_key = LV_KEY_RIGHT;
            break;
        case 5:
            act_key = LV_KEY_ENTER;
            break;
        }

        last_key = act_key;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }

    data->key = last_key;
}

/*Get the currently being pressed key.  0 if no key is pressed*/
static uint32_t keypad_get_key(void)
{
    /*Your code comes here*/
    return key_state;
    //return 0;
}


#else /*Enable this file at the top*/

/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif

main.cpp

#include <Arduino.h>
#include <TFT_eSPI.h>
#include <lvgl.h>
#include <lv_port_indev.h>

/*Change to your screen resolution*/
static const uint32_t screenWidth  = 160;
static const uint32_t screenHeight = 128;

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


//extern const lv_img_dsc_t picQiudayeda;

#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( ( uint16_t * )&color_p->full, w * h, true );
  tft.endWrite();

  lv_disp_flush_ready(disp);
}


void keyInit()//配置输入
{
  pinMode(0, INPUT_PULLUP);
  pinMode(32, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  pinMode(33, INPUT_PULLUP);
}

uint32_t keyScan()//扫描
{
   if(digitalRead(0)==0)
   {
     Serial.println("LV_KEY_NEXT");
     return LV_KEY_NEXT;
   }
      if(digitalRead(32)==0)
   {
      Serial.println("LV_KEY_PREV");
      return LV_KEY_PREV;

   }
      if(digitalRead(12)==0)
   {
     Serial.println("LV_KEY_ENTER");
     return LV_KEY_ENTER;

   }
      if(digitalRead(33)==0)
   {
     Serial.println("LV_KEY_LEFT");
     return LV_KEY_LEFT;
   }
   return 0;
}

void setup()
{  
    Serial.begin(115200);
    keyInit();
    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_draw_buf_init(&disp_buf, buf, NULL, screenWidth * 10);

    /*Initialize the display*/
    static 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.draw_buf = &disp_buf;
    lv_disp_drv_register(&disp_drv);

    lv_port_indev_init();//输入设备初始化

   
    lv_group_t *group = lv_group_create();

    lv_obj_t *obj =  lv_btn_create(lv_scr_act());
    lv_obj_t *obj2 = lv_btn_create(lv_scr_act());

    static lv_style_t style;
    lv_style_set_bg_color( &style, lv_color_hex(0xff0000));

    lv_style_set_width(&style, 60);
    lv_style_set_height(&style, 35);

    lv_obj_add_style(obj, &style, LV_PART_MAIN  );
    lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0 );

    lv_obj_add_style(obj2, &style, LV_PART_MAIN | LV_STATE_FOCUSED | LV_STATE_PRESSED );
    lv_obj_align_to(obj2, obj, LV_ALIGN_OUT_TOP_MID, 0, -5);

    lv_group_add_obj(group, obj);
    lv_group_add_obj(group, obj2);
    lv_indev_set_group(indev_keypad, group);

}

void loop()
{
  key_state = keyScan();
  lv_timer_handler(); /* let the GUI do its work */
  lv_task_handler();
  lv_tick_inc(5);   //有输入设备的时候打开
  delay(5);
}

4.squareline所见即所得

squareline官网 可以拖拽生成UI和事件可以导出界面和事件,目前不是特别成熟,可在拖拽生成UI后再修改,手动添加事件等。利用该软件可以快速的设计出UI,所见即所得。
在这里插入图片描述

  • 1
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ESP32移植LVGL,您可以按照以下步骤进行操作: 1. 获取LVGL源代码 您可以从LVGL的官方网站(https://lvgl.io)上下载LVGL源代码。然后,将其解压缩到ESP32的开发环境中。 2. 配置LVGLLVGL源代码的根目录中,有一个名为`lv_conf_template.h`的文件。请将其复制并重命名为`lv_conf.h`。然后,打开`lv_conf.h`文件,并对其进行适当的配置以满足您的特定需求。 3. 配置ESP32环境 在ESP32的开发环境中,您需要安装ESP-IDF(ESP32 IoT Development Framework)。您可以从ESP-IDF的官方网站(https://docs.espressif.com/projects/esp-idf/en/latest/)上找到下载和安装说明。 4. 添加LVGLESP32项目中 将LVGL源代码的`lvgl`文件夹和`lv_drivers`文件夹复制到您的ESP32项目中的`components`文件夹中。然后,在您的ESP32项目的`CMakeLists.txt`文件中添加以下内容: ``` set(EXTRA_COMPONENT_DIRS <path to lvgl>/lvgl <path to lvgl>/lv_drivers) ``` 5. 配置LVGL驱动程序 LVGL需要一个驱动程序来与您的显示硬件进行通信。您需要根据您的硬件配置LVGL驱动程序。您可以在LVGL源代码的`lv_drivers`文件夹中找到一些示例驱动程序,您可以根据您的硬件进行修改。 6. 初始化LVGL 在您的ESP32项目中,您需要初始化LVGL以便其可以与您的硬件通信。您可以使用以下代码进行初始化: ``` lv_init(); ``` 然后,您需要配置LVGL以使用您的驱动程序: ``` lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); disp_drv.disp_flush = your_flush_function; // 替换成您的驱动程序中的刷新函数 lv_disp_drv_register(&disp_drv); ``` 最后,您可以使用LVGL API来创建和管理您的GUI应用程序。 以上是在ESP32移植LVGL的基本步骤。具体实现可能因硬件和应用程序的不同而有所不同。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值