一、准备材料
- 开发板:esp32s3 
- idf版本:4.4.2 
- lvgl:8.3.3 
注意:lvgl不要选择master分支,编译失败时不好确定问题。
二、创建idf项目
方式一
通过 VSCode 创建项目
- 在命令面板中搜索 esp-idf new,开始创建项目  
- 项目配置信息  
- 选择需要的模板,也可以选择想要的案例  
注意: 需要VSCode 中安装 idf 环境的可以看我之前的笔记VSCode 中安装 esp-idf
方式二
通过命令创建项目
idf.py create-project
方式三
直接在路径 %ESP-IDF%\espressif\frameworks\esp-idf-v4.4.2\examples 中拷贝自己需要的使用案例
三、添加LVGL库
- 下载lvgl8.3.3 GitHub:https://github.com/lvgl/lvgl.git 
- 将lvgl库添加到项目的 components 文件中,如下图所示  ** 注意,如果觉得文件比较多的话,可以删除不用的文件,如下图所示: ** 注意,如果觉得文件比较多的话,可以删除不用的文件,如下图所示: 
- 将 - lv_conf_template.h重命名为- lv_conf_.h,并将文件中的- #if 0改为- #if 1 
- 设置开发板为esp32s3  
- 编译  ** 注意:**如果出现编译错误时,检查一下自己是否下载成了master分支 ** 注意:**如果出现编译错误时,检查一下自己是否下载成了master分支
四、添加显示驱动
- 下载lvgl_esp32_drivers GitHub:https://github.com/lvgl/lvgl_esp32_drivers 
- 使用命令 - idf.py menuconfig打开图形配置界面 
- 进入 Component config → LVGL ESP Drivers → LVGL TFT Display controller 配置显示驱动信息  
- SPI引脚配置  
- 进入 Component config → LVGL configguration 配置LVGL信息  
- 按s键进行保存,完成后按Q退出   
- 配置屏幕信息和SPI通道数量 编译后会产生以下错误,如果所示 未定义显示器的像素宽度和高度  - 未定义开发板 SPI 的通道数量  - 只需要在文件 - lvgl_helpers.h中添加以下定义即可,如图所示- #define SPI_HOST_MAX 3 // 开发板 SPI 通道数量 #define LV_HOR_RES_MAX 240 // 显示器水平像素 #define LV_VER_RES_MAX 320 // 显示器垂直像素  
- 编译通过后,在 main.c 文件中添加程序,程序后面附上 
- SPI 信道错误  - 运行后不断重启,并出现图示中的问题,值需要在文件 `` 中将SPI信道改为自动模式 - SPI_DMA_CH_AUTO即可,如下图所示: 
到此屏幕就能正常显示了。
五、添加触摸驱动
- 使用命令 - idf.py menuconfig打开图形配置界面 
- 进入 Component config → LVGL ESP Drivers → LVGL Touch controller 打开触摸驱动   
- 返回上一级,选择I2C通道   
- 进入 Component config → I2C Port Settings 配置I2C引脚  
六、main.c文件
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_freertos_hooks.h"
#include "freertos/semphr.h"
#include "esp_system.h"
#include "driver/gpio.h"
/* Littlevgl specific */
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#include "lvgl_helpers.h"
/*********************
 *      DEFINES
 *********************/
#define TAG "main"
#define LV_TICK_PERIOD_MS 1
/**********************
 *  STATIC PROTOTYPES
 **********************/
static void lv_tick_task(void *arg);
static void guiTask(void *pvParameter);
static void create_demo_application(void);
/**********************
 *   APPLICATION MAIN
 **********************/
void app_main() {
    /* 使用任务创建图形时,必须使用固定任务创建
     * 注意:不使用Wi-Fi或蓝牙时,可以将gui任务固定到核心0 */
    xTaskCreatePinnedToCore(guiTask, "gui", 4096*2, NULL, 0, NULL, 1);
}
/* Creates a semaphore to handle concurrent call to lvgl stuff
 * If you wish to call *any* lvgl function from other threads/tasks
 * you should lock on the very same semaphore! */
SemaphoreHandle_t xGuiSemaphore;
static void guiTask(void *pvParameter) {
    (void) pvParameter;
    xGuiSemaphore = xSemaphoreCreateMutex();
    lv_init();
    /* 初始化驱动程序使用的SPI或I2C总线 */
    lvgl_driver_init();
    /* 创建堆内存,用过图形缓冲区buf1 */
    lv_color_t* buf1 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1 != NULL);
    /* 不使用单色显示器时使用双缓冲 */
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
    lv_color_t* buf2 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf2 != NULL);
#else
    static lv_color_t *buf2 = NULL;
#endif
    static lv_disp_draw_buf_t disp_buf;
    uint32_t size_in_px = DISP_BUF_SIZE;
#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820         \
    || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A    \
    || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D     \
    || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306
    /* Actual size in pixels, not bytes. */
    size_in_px *= 8;
#endif
    /* Initialize the working buffer depending on the selected display.
     * NOTE: buf2 == NULL when using monochrome displays. */
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, size_in_px);
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.flush_cb = disp_driver_flush;
    /* When using a monochrome display we need to register the callbacks:
     * - rounder_cb
     * - set_px_cb */
#ifdef CONFIG_LV_TFT_DISPLAY_MONOCHROME
    disp_drv.rounder_cb = disp_driver_rounder;
    disp_drv.set_px_cb = disp_driver_set_px;
#endif
    disp_drv.draw_buf = &disp_buf;
    lv_disp_drv_register(&disp_drv);
    /* Register an input device when enabled on the menuconfig */
#if CONFIG_LV_TOUCH_CONTROLLER != TOUCH_CONTROLLER_NONE
    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.read_cb = touch_driver_read;
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    lv_indev_drv_register(&indev_drv);
#endif
    /* Create and start a periodic timer interrupt to call lv_tick_inc */
    const esp_timer_create_args_t periodic_timer_args = {
        .callback = &lv_tick_task,
        .name = "periodic_gui"
    };
    esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 1000));
    /* Create the demo application */
    create_demo_application();
    while (1) {
        /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
        vTaskDelay(pdMS_TO_TICKS(10));
        /* Try to take the semaphore, call lvgl related function on success */
        if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
            lv_task_handler();
            xSemaphoreGive(xGuiSemaphore);
       }
    }
    /* A task should NEVER return */
    free(buf1);
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
    free(buf2);
#endif
    vTaskDelete(NULL);
}
/**
 * @brief 创建标签
 */
void lvgl_lable_test(){
    /* 创建一个标签 */
    lv_obj_t* label = lv_label_create(lv_scr_act());
    if (NULL != label)
    {
        // lv_obj_set_x(label, 90);                         // 设置控件的X坐标
        // lv_obj_set_y(label, 100);                        // 设置控件的Y坐标
        // lv_obj_set_size(label, 60, 20);                  // 设置控件大小
        lv_label_set_text(label, "Counter");                // 初始显示 0
        // lv_obj_center(label);                            // 居中显示
        lv_obj_align(label, LV_ALIGN_CENTER, 0, -50);       // 居中显示后,向上偏移50
    }
}
/**
 * @brief 按钮事件回调函数
 */
static void btn_event_callback(lv_event_t* event)
{
    static uint32_t counter = 1;
    lv_obj_t* btn = lv_event_get_target(event);                 //获取事件对象
    if (btn != NULL)
    {
        lv_obj_t* btn_parent =  lv_obj_get_parent(btn);
        lv_obj_t* label = lv_obj_get_child(btn_parent, 0);
        lv_label_set_text_fmt(label, "%d", counter);            //设置显示内容
        lv_obj_align(label, LV_ALIGN_CENTER, 0, -50);           // 居中显示后,向上偏移50
        counter++;
    }
}
/**
 * @brief 创建按钮
 */
void lvgl_button_test(){
    /* 在当前界面中创建一个按钮 */
    lv_obj_t* btn = lv_btn_create(lv_scr_act());                                        // 创建Button对象
    if (btn != NULL)
    {
        lv_obj_set_size(btn, 80, 20);                                                   // 设置对象宽度和高度
        // lv_obj_set_pos(btn, 90, 200);                                                // 设置按钮的X和Y坐标
        lv_obj_add_event_cb(btn, btn_event_callback, LV_EVENT_CLICKED, NULL);           // 给对象添加CLICK事件和事件处理回调函数
        lv_obj_align(btn, LV_ALIGN_CENTER, 0, 50);                                      // 居中显示后,向下偏移50
        lv_obj_t* btn_label = lv_label_create(btn);                                     // 基于Button对象创建Label对象
        if (btn_label != NULL)
        {
            lv_label_set_text(btn_label, "button");                                     // 设置显示内容
            lv_obj_center(btn_label);                                                   // 对象居中显示
        }
    }
}
static void create_demo_application(void)
{
    /* 加载标签 */
    lvgl_lable_test();
    /* 加载按钮 */
    lvgl_button_test();
}
static void lv_tick_task(void *arg) {
    (void) arg;
    lv_tick_inc(LV_TICK_PERIOD_MS);
}标签: # esp

留言评论