基于IDF的ESP32S3-LVGL DEMO移植

简介

ESP32-32出色的性价比,较好的性能与内存空间,可以好利用来完成GUI显示库的加载

LVGL

LVGL是一款比较流行的致力于MCU与MPU创建漂亮UI的嵌入式图形库,免费且开源。

硬件

硬件采用的是正点原子的ESP32-S3
屏幕使用的是SPI通信方式,配合IO口控制(RST,A0),来实现LCD屏幕的驱动

移植步骤

LVGL移植总的步骤主要是如下几步
1.调用lv_init();
2.初始化驱动
3.注册显示与输入驱动,显存的配置,显示响应回调函数的响应
4.lv_tick_inc(x) 在中断中定时更新,x设定取决于lv_tick_inc的调用频率
5.lv_timer_handler,定时调用,完成LVGL的响应(更新LVGL的响应)

具体示例

拷贝一个可以正常驱动LCD的工程

拷贝LVGL(V8.3.0)代码至工程,ESP32需要在指定的路径components下

由于ESP32并不需要去修改lv_conf.h这个文件来配置LVGL,可以通过设置项来修改LVGL的配置,具体的配置机制原理未深入了解

配置LVGL

在默认的设置下勾选MUSIC DEMO,由于MUSCI DEMO中还用到了其他字体,还需要勾选 Montserrat 12与Montserrat 16这两种字体


此时编译工程,应该是可以编译通过

移植LVGL相关代码

SemaphoreHandle_t xGuiSemaphore;

void lvgl_demo(void)
{
    lv_init();            //LVGL初始化前都需要调用
    lv_port_disp_init();  //显示驱动的移植,初始化及配对,输出  
    lv_port_indev_init(); //输入驱动的移植

    xGuiSemaphore = xSemaphoreCreateMutex();

    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, 1 * 1000)); //创建定时器,更新LVGL的内部时钟基准

    lv_demo_music();
    while(1)
    {
        if( pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY))
        {
            lv_timer_handler(); //定时调用,更新LVGL的实现
            xSemaphoreGive(xGuiSemaphore);
        }
        
        vTaskDelay(pdMS_TO_TICKS(5));
    }
}

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

显示初始化与绑定代码

void lv_port_disp_init(void)
{
    void *buf1 = NULL;

    buf1 = heap_caps_malloc(lcd_self.width * 10 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); //申请显存

    static lv_disp_draw_buf_t disp_buf;
    lv_disp_draw_buf_init(&disp_buf, buf1, NULL, lcd_self.width * 10); //绑定显存,显存绑定有多种形式,这是一种开销最小的单显存设置

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);

    disp_drv.hor_res = lcd_self.width; //屏幕宽度
    disp_drv.ver_res = lcd_self.height; //屏幕高度

    disp_drv.flush_cb = lvgl_disp_flush_cb;

    disp_drv.draw_buf = &disp_buf;

    lv_disp_drv_register(&disp_drv); //初始化显示屏幕尺寸,并绑定屏幕更新函数
}

static void lvgl_disp_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
    lcd_color_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t*)color_map);//显示屏区域更新像素点颜色,像素颜色在数组中
    lv_disp_flush_ready(drv); //发送更新完毕,这一句代码必须要有
}

输出驱动里面如果是空的也可以完成显示的移植,只是没有输入功能
demo示例,参考的正点原子提供的代码,有没有都不影响MUSIC DEMO的显示
MUSCI DEMO对这几个按键也没有响应,加上了,也无法判断是否已正常工作

void lv_port_indev_init(void)
{
    static lv_indev_drv_t indev_drv;

    keypad_init();

    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);
}

uint32_t g_last_key = 0;

void keypad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
    uint32_t act_key = keypad_get_key();

    if(act_key != 0)
    {
        data->state = LV_INDEV_STATE_PR;

        switch(act_key)
        {
            case KEY0_PRES:
                act_key = LV_KEY_RIGHT;
                break;
            case KEY1_PRES:
                act_key = LV_KEY_NEXT;
                //back_act_key = KEY1_PRES;
                break;
            case KEY2_PRES:
                act_key = LV_KEY_LEFT;
                break;
            case KEY3_PRES:
                act_key = LV_KEY_ENTER;
                break;
            default:
                break;
        }

        g_last_key = act_key;
    }
    else
    {
        data->state = LV_INDEV_STATE_REL;
        g_last_key = 0;
    }

    data->key = g_last_key;
}

uint32_t keypad_get_key(void)
{
    return xl9555_key_scan(0);
}

显示效果

V9.1.0移植

大体步骤与V8.3.0一致,期间出了一些问题,在这里也记录下

屏幕驱动API有些变化,更新程序的形参考声明有所变化,直接转换成uint16_t*的16位RGB565颜色也可以正常显示,暂时没有深究具体原因

void lv_port_disp_init(void)
{
    void *buf1 = NULL;
    buf1 = heap_caps_malloc(lcd_self.width * 10 * 4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);

    lv_display_t *disp = lv_display_create(lcd_self.width, lcd_self.height);
    lv_display_set_flush_cb(disp, lvgl_disp_flush_cb);

    lv_display_set_buffers(disp, buf1, NULL, lcd_self.width * 10 * 4, LV_DISPLAY_RENDER_MODE_PARTIAL);
}

static void lvgl_disp_flush_cb(lv_disp_t *drv, const lv_area_t *area, uint8_t *px_map)
{
    lcd_color_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t*)px_map);
    lv_disp_flush_ready(drv);
}

无法正常显示,提示栈溢出

原因在于任务栈太小了,导致任务运行出错,增大任务栈即可正常工作。LVGL的使用,相对来说使用的STACK较深,系统默认的配置就不太够用

总结

LVGL需要配置显存跟内存,同时也需要考虑任务栈的问题,使用的过程中一定也会遇到很多问题,资料相对较少要自己多加摸索。
经过上述步骤 LVGL可以在ESP32上面运行DEMO,不过这个离会用LVGL还有一定的距离。
还需要查看更多的LVGL资料,以及ESP32-IDF编程的资料,才能进一步熟悉并使用。

posted @ 2024-09-21 15:46  cau_par  阅读(463)  评论(0编辑  收藏  举报