ESP32运行LVGLv9.2

摘要

使用正点原子ESP32开发板学习LVGL时发现正点原子的LVGL开发指南里的内容好像不能直接用于新版本的LVGL(9.2版本),于是自己学了一下LVGL的新API,结合正点原子原来的例程成功运行LVGL,做了一个简单的小测试程序,基于正点原字的示例代码中的24_touch示例工程。

1. 功能说明

整体效果如下图所示,整个画面有三个组件组成,使用下方两个按键控制上方的进度条。程序中控制的其实是一个变量,在实际应用中改成需要被控制的变量即可,例如PWM的占空比控制LED亮度。

9367803a5a214782838e0e94f87acf69.jpeg

2. 硬件说明

2.1 开发板

正点原子DNESP32S3开发板。

2.2 屏幕

正点原子4.3寸RGBLCD电容触摸屏800*480。

3. 代码

3.1 移植LVGL

esp32idf移植LVGL非常简单,如下图中所示,只需要在组件管理里搜LVGL然后直接install就行

1. 点idf的欢迎界面中最后一个按钮进入组件管理

8ed26ddbaeca44058151f50f4149c950.png

2. 搜索LVGL

2b1918884f6b490297e0ab56a568b482.png

3. 选择这个lvgl,点进去

b31ae613e5ca427585e197e3e840d76a.png

4. 点安装。

944337dde0b746bf974441b25fcad8c5.png

点击install后LVGL就已经移植好了,idf会帮我们自动把cmakefile和其他的东西都弄好。

下面是与其他平台不同的注意点:

不要动lv_conf_template.h文件,这个是esp组件已经改好的东西了。

想要更改LVGL的设置也不需要改lv_conf_template.h文件,而是在SDK configuration中配置LVGL操作如下图所示。

1. 点vscode最下方蓝色一排的那个小齿轮

edf5eb6da45b4cb7970c9866c1f79ab6.png

2. 在打开的SDK configuration中看左边目录,往下翻找到LVGL配置

076f7d0fd1564f99981e3951ff884377.png

这边就是设置LVGL参数的地方,在这个示例中用默认配置就行,这边就不改了。

3.2 主要代码

3.2.1 LCD接口函数

首先LVGL自带字库和很多图形,所以LCD屏只需要留一个初始化和一些基础的函数就行,其他画字和画字符串的函数可以删掉,不删也没事。

LVGL需要一个往屏幕上批量打点的函数接口,在24_touch中没有这样的函数,正点原子教程中直接调用了一个esp_lcd_panel的底层函数,但是这样需要引用头文件啥的,反正很麻烦,我直接在LCD屏中新做了一个给LVGL刷点的函数。如下图。

03504f83fe4e4f3db6ca2916716b3458.png

9acc53b8b0284313b0d8f68ed5e2c645.png

图中代码如下:

void ltdc_draw_lvgl(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t * color)
{
    taskENTER_CRITICAL(&my_spinlock);
    esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2, y2, color);
    taskEXIT_CRITICAL(&my_spinlock);
}

3.2.2 主程序代码

然后修改main.c,代码量较大就不放图了,直接给代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "led.h"
#include "ltdc.h"
#include "touch.h"
#include "lvgl.h"
#include "esp_timer.h"

i2c_obj_t i2c0_master;
uint16_t num;
lv_obj_t * bar1;

static void lvgl_disp_flush_cb(lv_display_t *display, const lv_area_t *area, lv_color16_t *px_map)
{
    ltdc_draw_lvgl(area->x1, area->y1, area->x2 + 1, area->y2 + 1, px_map);
    lv_disp_flush_ready(display);
}

static void increase_lvgl_tick(void *arg)
{
    lv_tick_inc(10);
}

void lcd_init()
{
    lv_init();
    ltdc_init();
    void *buf1 = NULL;
    void *buf2 = NULL;
    buf1 = heap_caps_malloc(ltdcdev.width * 60 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    buf2 = heap_caps_malloc(ltdcdev.width * 60 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    lv_display_t *display1 = lv_display_create(ltdcdev.width, ltdcdev.height);
    lv_display_set_buffers(display1, buf1, buf2, ltdcdev.width * 60, LV_DISPLAY_RENDER_MODE_PARTIAL);
    lv_display_set_flush_cb(display1, lvgl_disp_flush_cb);

    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &increase_lvgl_tick,
        .name = "lvgl_tick"};
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, 10 * 1000));
}

static bool touchpad_is_pressed(void)
{
    tp_dev.scan(0); /* 触摸按键扫描 */
    if (tp_dev.sta & TP_PRES_DOWN)
    {
        return true;
    }
    return false;
}
static void touchpad_get_xy(lv_coord_t *x, lv_coord_t *y)
{
    (*x) = tp_dev.x[0];
    (*y) = tp_dev.y[0];
}

void touchpad_read(lv_indev_t * indev, lv_indev_data_t * data)
{
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;
    if (touchpad_is_pressed())
    {
        touchpad_get_xy(&last_x, &last_y);
        data->state = LV_INDEV_STATE_PRESSED;
    }
    else
    {
        data->state = LV_INDEV_STATE_RELEASED;
    }
    data->point.x = last_x;
    data->point.y = last_y;
}

void touch_init()
{
    tp_dev.init();
    lv_indev_t * indev = lv_indev_create();
    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
    lv_indev_set_read_cb(indev, touchpad_read);
}

void lv_example_bar_1(void)
{
    bar1 = lv_bar_create(lv_screen_active());
    lv_obj_set_size(bar1, 200, 20);
    lv_obj_align(bar1, LV_ALIGN_CENTER, 0, -50);
    lv_bar_set_value(bar1, 50, LV_ANIM_OFF);
}

static void event_handler(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);

    if(code == LV_EVENT_CLICKED) {
        //printf("Clicked\r\n");
        if(num<100)
            num += 10;
        lv_bar_set_value(bar1, num, LV_ANIM_OFF);
    }
}
static void event_handler2(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);

    if(code == LV_EVENT_CLICKED) {
        //printf("Clicked\r\n");
        if(num>0)
            num -= 10;
        lv_bar_set_value(bar1, num, LV_ANIM_OFF);
    }
}
void lv_example_button_1(void)
{
    lv_obj_t * label;

    lv_obj_t * btn1 = lv_button_create(lv_screen_active());
    lv_obj_add_event_cb(btn1, event_handler, LV_EVENT_ALL, NULL);
    lv_obj_align(btn1, LV_ALIGN_CENTER, 100, 100);
    lv_obj_add_flag(btn1, LV_OBJ_FLAG_CLICKABLE);
    label = lv_label_create(btn1);
    lv_label_set_text(label, "btn1");
    lv_obj_center(label);

    lv_obj_t * btn2 = lv_button_create(lv_screen_active());
    lv_obj_add_event_cb(btn2, event_handler2, LV_EVENT_ALL, NULL);
    lv_obj_align(btn2, LV_ALIGN_CENTER, -100, 100);
    lv_obj_add_flag(btn2, LV_OBJ_FLAG_CLICKABLE);
    label = lv_label_create(btn2);
    lv_label_set_text(label, "btn2");
    lv_obj_center(label);
}

void app_main(void)
{
    esp_err_t ret;
    ret = nvs_flash_init(); /* 初始化NVS */
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    num = 50;
    i2c0_master = iic_init(I2C_NUM_0); /* 初始化IIC0 */
    xl9555_init(i2c0_master);          /* 初始化XL9555 */
    lcd_init();
    touch_init();

    lv_example_button_1();
    lv_example_bar_1();
    while (1)
    {
        lv_timer_handler();
        vTaskDelay(10);
    }
}

整段代码直接复制到main.c中就可以运行出开头的效果。

3.3 其他说明

这里为了简单,没有对num做锁,实际应用时,可以改成freertos的通知,队列或者信号量等,对变量控制更安全一些。

因为加载的慢,要白屏很久才能显示内容。

 

 

### ESP32 使用 LVGL 图形库进行开发 #### 硬件与软件准备 为了在ESP32上使用LVGL图形库,需先准备好相应的硬件和软件环境。硬件方面主要涉及支持SPI接口的TFT显示屏;而软件部分则依赖Arduino IDE作为开发平台,并安装必要的库文件来实现对显示设备的支持[^1]。 #### 驱动LCD TFT_eSPI 针对TFT液晶屏的操作,推荐采用`TFT_eSPI`库完成基础驱动工作。该库提供了丰富的API用于控制屏幕属性以及绘制基本图形元素等功能。通过调整配置参数可以适配不同型号的显示器,确保最佳性能表现。 ```cpp #include <TFT_eSPI.h> // Hardware-specific library TFT_eSPI tft = TFT_eSPI(); /* Create a TFT class object, which is used to drive the screen */ void setup() { tft.init(); } ``` #### 编辑配置文件 对于LVGL而言,其初始化过程中的诸多选项可通过修改`lv_conf_template.h`来进行定制化设定。这一步骤至关重要,因为它决定了后续GUI界面渲染的具体行为模式,比如颜色深度、抗锯齿效果等特性开关状态均在此处定义。 ```c /* Copy this file as lv_conf.h*/ #define LV_COLOR_DEPTH 16 /* Color depth of the display (1/8/16) */ #define USE_LV_TINYGL 0 /* Enable/disable TinyGL rendering engine */ ... ``` #### 主程序入口 `main.cpp` 最后,在项目主文件中引入并调用上述组件实例化的对象方法即可启动整个应用程序逻辑流程。这里需要注意的是要合理安排任务调度机制以维持系统的实时响应能力,同时也要兼顾资源占用情况优化整体效率。 ```cpp // main.cpp extern "C" void app_main(void){ ... lv_init(); tft.begin(); static lv_disp_buf_t disp_buf; lv_color_t buf[ DISP_BUF_SIZE ]; lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); ... while(1){ lv_task_handler(); vTaskDelay(pdMS_TO_TICKS(5)); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值