【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        既然屏幕的驱动、触控的驱动也好了,那么下面要做的就是移植lvgl。其实不用lvgl,自己直接操作屏幕开发也是可以的,但就是比较麻烦,开发起来也不是很美观,看上去并不象一个产品。所以,一般驱动ok了,就会移植lvgl。

1、lvgl vs qt

        由于版权的关系,lvgl用的比较多。而目前我们开发的环境是mcu平台,所以基本上还是lvgl为主。

2、lvgl版本

        这里选用的版本不是最新的,而是8.3.11,但已经可以满足我们需求了。

3、更新idf_component.yml文件

        这部分,主要是加上后面我们需要引入的库,

## IDF Component Manager Manifest File
dependencies:
  ## Required IDF version
  idf:
    version: '>=4.1.0'
  # # Put list of dependencies here
  # # For components maintained by Espressif:
  # component: "~1.0.0"
  # # For 3rd party components:
  # username/component: ">=1.0.0,<2.0.0"
  # username2/component2:
  #   version: "~1.0.0"
  #   # For transient dependencies `public` flag can be set.
  #   # `public` flag doesn't have an effect dependencies of the `main` component.
  #   # All dependencies of `main` are public by default.
  #   public: true
  lvgl/lvgl:
    version: "^8.3.11"
  espressif/esp_lvgl_port:
    version: "^2.3.0"


4、删除不必要的CMakeLists.txt内容

        和大家想的不一样,这里要尽量保证不引入额外的库,

idf_component_register(SRCS "main.c" 
                    INCLUDE_DIRS ".")

5、用ai编写demo文件

        因为是lvgl demo,我们可以要求ai编写一个进度条的demo,同时在进度条更新的时候更新label。实际测试的时候,可能会出现各种问题,比如lcd屏幕拿错、接线接错、频率设置错、函数位置放错等等。所以,实际开发的时候,反反复复是常有的事情,大家还是需要耐心一点。注意,触摸的pin都要连上。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_timer.h"

#include "lvgl.h"

// ================= CONFIG =================
#define LCD_W 480
#define LCD_H 320

#define PIN_MOSI 13
#define PIN_CLK  14
#define PIN_CS   15
#define PIN_DC   2
#define PIN_RST  4
#define PIN_BL   12

#define TP_MOSI  23
#define TP_MISO  19
#define TP_CLK   18
#define TP_CS    5
#define TP_IRQ   27

static spi_device_handle_t spi_lcd;
static spi_device_handle_t spi_tp;

static const char *TAG = "ILI9488_LVGL";

// ======================================================
// GPIO control
// ======================================================
static inline void dc_cmd(void)  { gpio_set_level(PIN_DC, 0); }
static inline void dc_data(void) { gpio_set_level(PIN_DC, 1); }

static void lcd_reset(void)
{
    gpio_set_level(PIN_RST, 0);
    vTaskDelay(pdMS_TO_TICKS(100));
    gpio_set_level(PIN_RST, 1);
    vTaskDelay(pdMS_TO_TICKS(150));
}

// ======================================================
// SPI CMD / DATA
// ======================================================
static void lcd_cmd(uint8_t cmd)
{
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd,
    };
    dc_cmd();
    spi_device_polling_transmit(spi_lcd, &t);
}

static void lcd_data(const void *data, int len)
{
    spi_transaction_t t = {
        .length = len * 8,
        .tx_buffer = data,
    };
    dc_data();
    spi_device_polling_transmit(spi_lcd, &t);
}

// ======================================================
// ILI9488 INIT (stable version)
// ======================================================
static void ili9488_init(void)
{
    lcd_reset();

    lcd_cmd(0x01); // Software reset
    vTaskDelay(pdMS_TO_TICKS(120));

    lcd_cmd(0x11); // Sleep out
    vTaskDelay(pdMS_TO_TICKS(120));

    // RGB565 mode (important for stability)
    lcd_cmd(0x3A);
    uint8_t pix = 0x66;
    lcd_data(&pix, 1);

    // MADCTL (display orientation)
    lcd_cmd(0x36);
    uint8_t mad = 0x28;   // change to 0x28 if upside-down
    lcd_data(&mad, 1);

    lcd_cmd(0x29); // Display ON
    vTaskDelay(pdMS_TO_TICKS(50));

    ESP_LOGI(TAG, "LCD init OK");
}

// ======================================================
// Set drawing window
// ======================================================
static void set_window(int x1,int y1,int x2,int y2)
{
    uint8_t d[4];

    lcd_cmd(0x2A);
    d[0]=x1>>8; d[1]=x1;
    d[2]=x2>>8; d[3]=x2;
    lcd_data(d,4);

    lcd_cmd(0x2B);
    d[0]=y1>>8; d[1]=y1;
    d[2]=y2>>8; d[3]=y2;
    lcd_data(d,4);

    lcd_cmd(0x2C);
}

// ======================================================
// Touch read (XPT2046 style)
// ======================================================
static uint16_t tp_read(uint8_t cmd)
{
    uint8_t tx[3] = {cmd,0,0};
    uint8_t rx[3] = {0};

    spi_transaction_t t = {
        .length = 24,
        .tx_buffer = tx,
        .rx_buffer = rx,
    };

    spi_device_polling_transmit(spi_tp, &t);

    return ((rx[1] << 8) | rx[2]) >> 3;
}

static void touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
    if (gpio_get_level(TP_IRQ) == 0)
    {
        data->state = LV_INDEV_STATE_PRESSED;
        data->point.x = tp_read(0xD0);
        data->point.y = tp_read(0x90);
    }
    else
    {
        data->state = LV_INDEV_STATE_RELEASED;
    }
}

// ======================================================
// LVGL flush (stable version)
// ======================================================
static uint8_t line_buf[480 * 3];

static void lcd_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    int w = area->x2 - area->x1 + 1;
    int h = area->y2 - area->y1 + 1;

    set_window(area->x1, area->y1, area->x2, area->y2);

    dc_data();

    spi_transaction_t t = {
        .length = w * 3 * 8,
        .tx_buffer = line_buf,
    };

    for (int y = 0; y < h; y++)
    {
        for (int x = 0; x < w; x++)
        {
            uint16_t c = color_p[y * w + x].full;

            line_buf[x*3+0] = ((c >> 11) & 0x1F) << 3;
            line_buf[x*3+1] = ((c >> 5)  & 0x3F) << 2;
            line_buf[x*3+2] = (c & 0x1F) << 3;
        }

        spi_device_polling_transmit(spi_lcd, &t);
    }

    lv_disp_flush_ready(disp);
}

// ======================================================
// UI
// ======================================================
static lv_obj_t *bar;
static lv_obj_t *label;
static int progress = 0;

static void ui_create(void)
{
    bar = lv_bar_create(lv_scr_act());
    lv_obj_set_size(bar, 300, 25);
    lv_obj_align(bar, LV_ALIGN_CENTER, 0, -30);
    lv_bar_set_range(bar, 0, 100);

    // Create label to show percentage
    label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "progress: 0%");
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 20);
    lv_obj_set_style_text_font(label, &lv_font_montserrat_14, LV_PART_MAIN);
}

// ================= LVGL Tick Timer =================
static void lv_tick_cb(void *arg)
{
    lv_tick_inc(1); // LVGL 1ms tick
}

static void progress_task(void *arg)
{
    char buf[32];
    while (1)
    {
        progress++;
        if (progress > 100) progress = 0;

        lv_bar_set_value(bar, progress, LV_ANIM_ON);

        // Update percentage label
        sprintf(buf, "progress: %d%%", progress);
        lv_label_set_text(label, buf);

        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

// ======================================================
// Backlight control
// ======================================================
static void backlight_init(void)
{
    gpio_config_t io = {
        .pin_bit_mask = 1ULL << PIN_BL,
        .mode = GPIO_MODE_OUTPUT
    };
    gpio_config(&io);
    gpio_set_level(PIN_BL, 1);
}

// ======================================================
// MAIN ENTRY
// ======================================================
void app_main(void)
{
    gpio_set_direction(PIN_DC, GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_RST, GPIO_MODE_OUTPUT);

    // ================= LCD SPI =================
    spi_bus_config_t bus_lcd = {
        .mosi_io_num = PIN_MOSI,
        .miso_io_num = -1,
        .sclk_io_num = PIN_CLK,
        .max_transfer_sz = 1024 * 10
    };

    spi_bus_initialize(SPI2_HOST, &bus_lcd, SPI_DMA_CH_AUTO);

    spi_device_interface_config_t dev_lcd = {
        .clock_speed_hz = 20 * 1000 * 1000, // 20MHz
        .mode = 0,
        .spics_io_num = PIN_CS,
        .queue_size = 7,
    };

    spi_bus_add_device(SPI2_HOST, &dev_lcd, &spi_lcd);

    // ================= TOUCH SPI =================
    spi_bus_config_t bus_tp = {
        .mosi_io_num = TP_MOSI,
        .miso_io_num = TP_MISO,
        .sclk_io_num = TP_CLK,
        .max_transfer_sz = 32
    };

    spi_bus_initialize(SPI3_HOST, &bus_tp, SPI_DMA_CH_AUTO);

    spi_device_interface_config_t dev_tp = {
        .clock_speed_hz = 2 * 1000 * 1000,
        .mode = 0,
        .spics_io_num = TP_CS,
        .queue_size = 3,
    };

    spi_bus_add_device(SPI3_HOST, &dev_tp, &spi_tp);

    gpio_set_direction(TP_IRQ, GPIO_MODE_INPUT);

    // ================= LCD INIT =================
    ili9488_init();
    backlight_init();

    // ================= LVGL INIT =================
    lv_init();

    static lv_color_t *buf1;
    buf1 = heap_caps_malloc(LCD_W * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);

    static lv_disp_draw_buf_t draw_buf;
    lv_disp_draw_buf_init(&draw_buf, buf1, NULL, LCD_W * 20);

    // LVGL tick timer (1ms)
    esp_timer_handle_t timer;
    const esp_timer_create_args_t tick_args = {
        .callback = &lv_tick_cb,
        .name = "lv_tick"
    };
    esp_timer_create(&tick_args, &timer);
    esp_timer_start_periodic(timer, 1000); // 1ms

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = LCD_W;
    disp_drv.ver_res = LCD_H;
    disp_drv.flush_cb = lcd_flush;
    disp_drv.draw_buf = &draw_buf;
    disp_drv.full_refresh = 1;

    lv_disp_drv_register(&disp_drv);

    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = touch_read;
    lv_indev_drv_register(&indev_drv);

    ui_create(); // Create LVGL UI

    xTaskCreate(progress_task, "progress", 4096, NULL, 5, NULL);

    while (1)
    {
        lv_timer_handler(); // LVGL main loop
        vTaskDelay(pdMS_TO_TICKS(5));
    }
}

6、编译和测试

        实际开发的时候,还有一种debug方法比较好,那就是可以参考之前2.8寸屏时的demo。有的时候对比测试,反而效率更高。至于编译、烧录,这都是基本操作了。

7、lvgl继续引入触摸机制

        和一般屏幕相比较,触摸屏的优势显而易见。所以lvgl port之后,我们可以快速把触摸屏移植到lvgl,主要就是一个touch_read回调函数。这个回调函数里面,难的不是电压计算,而是如何把电压换算成坐标。毕竟屏幕的旋转,是经常遇到的事情。

        搞定了屏幕位置读取,以及位置换算之后,就可以写两个按钮,看看触摸屏和lvgl结合起来,应用效果如何。当然,这里面主要的代码,还是ai来写。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_timer.h"

#include "lvgl.h"

static void lvgl_task(void *arg);

// ================= CONFIG =================
#define LCD_W 480
#define LCD_H 320

#define PIN_MOSI 13
#define PIN_CLK  14
#define PIN_CS   15
#define PIN_DC   2
#define PIN_RST  4
#define PIN_BL   12

#define TP_MOSI  23
#define TP_MISO  19
#define TP_CLK   18
#define TP_CS    5
#define TP_IRQ   27

static spi_device_handle_t spi_lcd;
static spi_device_handle_t spi_tp;

static const char *TAG = "ILI9488_LVGL";

// ======================================================
// GPIO control
// ======================================================
static inline void dc_cmd(void)  { gpio_set_level(PIN_DC, 0); }
static inline void dc_data(void) { gpio_set_level(PIN_DC, 1); }

static void lcd_reset(void)
{
    gpio_set_level(PIN_RST, 0);
    vTaskDelay(pdMS_TO_TICKS(100));
    gpio_set_level(PIN_RST, 1);
    vTaskDelay(pdMS_TO_TICKS(150));
}

// ======================================================
// SPI CMD / DATA
// ======================================================
static void lcd_cmd(uint8_t cmd)
{
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd,
    };
    dc_cmd();
    spi_device_polling_transmit(spi_lcd, &t);
}

static void lcd_data(const void *data, int len)
{
    spi_transaction_t t = {
        .length = len * 8,
        .tx_buffer = data,
    };
    dc_data();
    spi_device_polling_transmit(spi_lcd, &t);
}

// ======================================================
// ILI9488 INIT (stable version)
// ======================================================
static void ili9488_init(void)
{
    lcd_reset();

    lcd_cmd(0x01); // Software reset
    vTaskDelay(pdMS_TO_TICKS(120));

    lcd_cmd(0x11); // Sleep out
    vTaskDelay(pdMS_TO_TICKS(120));

    // RGB565 mode (important for stability)
    lcd_cmd(0x3A);
    uint8_t pix = 0x66;
    lcd_data(&pix, 1);

    // MADCTL (display orientation)
    lcd_cmd(0x36);
    uint8_t mad = 0x28;   // change to 0x28 if upside-down
    lcd_data(&mad, 1);

    lcd_cmd(0x29); // Display ON
    vTaskDelay(pdMS_TO_TICKS(50));

    ESP_LOGI(TAG, "LCD init OK");
}

// ======================================================
// Set drawing window
// ======================================================
static void set_window(int x1,int y1,int x2,int y2)
{
    uint8_t d[4];

    lcd_cmd(0x2A);
    d[0]=x1>>8; d[1]=x1;
    d[2]=x2>>8; d[3]=x2;
    lcd_data(d,4);

    lcd_cmd(0x2B);
    d[0]=y1>>8; d[1]=y1;
    d[2]=y2>>8; d[3]=y2;
    lcd_data(d,4);

    lcd_cmd(0x2C);
}

// ======================================================
// Touch read (XPT2046 style)
// ======================================================
static uint16_t tp_read(uint8_t cmd)
{
    uint8_t tx[3] = {cmd,0,0};
    uint8_t rx[3] = {0};

    spi_transaction_t t = {
        .length = 24,
        .tx_buffer = tx,
        .rx_buffer = rx,
    };

    spi_device_polling_transmit(spi_tp, &t);

    return ((rx[1] << 8) | rx[2]) >> 3;
}

static void touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
    static bool touched = false;

    if (gpio_get_level(TP_IRQ) == 0)
    {
        data->state = LV_INDEV_STATE_PRESSED;
        
        // Read raw touch coordinates
        uint16_t x_raw = tp_read(0xD0);
        uint16_t y_raw = tp_read(0x90);
        
        // Calibrate and convert to screen coordinates
        // Swap X and Y, and invert Y axis for correct orientation
        // Adjust these values based on your touch panel calibration
        int x = (LCD_W * (3900 - y_raw)) / 3900; // very critical, by feixiaoxing
        int y = LCD_H - 1 - (LCD_H * (x_raw - 100)) / 3900;
        
        // Clamp values
        if (x < 0) x = 0;
        if (x >= LCD_W) x = LCD_W - 1;
        if (y < 0) y = 0;
        if (y >= LCD_H) y = LCD_H - 1;
        
        data->point.x = x;
        data->point.y = y;
        
        if (!touched) {
            ESP_LOGI(TAG, "Touch: x=%d, y=%d (raw: x=%d, y=%d)", 
                     data->point.x, data->point.y, x_raw, y_raw);
            touched = true;
        }
    }
    else
    {
        data->state = LV_INDEV_STATE_RELEASED;
        touched = false;
    }
}

// ======================================================
// LVGL flush (stable version)
// ======================================================
static uint8_t line_buf[480 * 3];

static void lcd_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    int w = area->x2 - area->x1 + 1;
    int h = area->y2 - area->y1 + 1;

    set_window(area->x1, area->y1, area->x2, area->y2);

    dc_data();

    spi_transaction_t t = {
        .length = w * 3 * 8,
        .tx_buffer = line_buf,
    };

    for (int y = 0; y < h; y++)
    {
        for (int x = 0; x < w; x++)
        {
            uint16_t c = color_p[y * w + x].full;

            line_buf[x*3+0] = ((c >> 11) & 0x1F) << 3;
            line_buf[x*3+1] = ((c >> 5)  & 0x3F) << 2;
            line_buf[x*3+2] = (c & 0x1F) << 3;
        }

        spi_device_polling_transmit(spi_lcd, &t);
    }

    lv_disp_flush_ready(disp);
}

// ======================================================
// UI
// ======================================================
static lv_obj_t *bar;
static lv_obj_t *label;
static lv_obj_t *btn_inc;
static lv_obj_t *btn_dec;
static int progress = 0;

// Button callback functions
static void btn_inc_event_cb(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e);
    if (code == LV_EVENT_CLICKED) {
        if (progress < 100) {
            progress++;
            char buf[32];
            sprintf(buf, "progress: %d%%", progress);
            lv_label_set_text(label, buf);
            lv_bar_set_value(bar, progress, LV_ANIM_ON);
        }
    }
}

static void btn_dec_event_cb(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e);
    if (code == LV_EVENT_CLICKED) {
        if (progress > 0) {
            progress--;
            char buf[32];
            sprintf(buf, "progress: %d%%", progress);
            lv_label_set_text(label, buf);
            lv_bar_set_value(bar, progress, LV_ANIM_ON);
        }
    }
}

static void ui_create(void)
{
    // Create progress bar
    bar = lv_bar_create(lv_scr_act());
    lv_obj_set_size(bar, 300, 30);
    lv_obj_align(bar, LV_ALIGN_CENTER, 0, -60);
    lv_bar_set_range(bar, 0, 100);
    lv_bar_set_value(bar, 0, LV_ANIM_OFF);

    // Create label to show percentage
    label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "progress: 0%");
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_style_text_font(label, &lv_font_montserrat_14, LV_PART_MAIN);

    // Create increment button
    btn_inc = lv_btn_create(lv_scr_act());
    lv_obj_set_size(btn_inc, 100, 40);
    lv_obj_align(btn_inc, LV_ALIGN_CENTER, -80, 60);
    lv_obj_add_event_cb(btn_inc, btn_inc_event_cb, LV_EVENT_ALL, NULL);

    lv_obj_t *label_inc = lv_label_create(btn_inc);
    lv_label_set_text(label_inc, "+");
    lv_obj_center(label_inc);

    // Create decrement button
    btn_dec = lv_btn_create(lv_scr_act());
    lv_obj_set_size(btn_dec, 100, 40);
    lv_obj_align(btn_dec, LV_ALIGN_CENTER, 80, 60);
    lv_obj_add_event_cb(btn_dec, btn_dec_event_cb, LV_EVENT_ALL, NULL);

    lv_obj_t *label_dec = lv_label_create(btn_dec);
    lv_label_set_text(label_dec, "-");
    lv_obj_center(label_dec);
}

// ================= LVGL Tick Timer =================
static void lv_tick_cb(void *arg)
{
    lv_tick_inc(1); // LVGL 1ms tick
}

// ======================================================
// Backlight control
// ======================================================
static void backlight_init(void)
{
    gpio_config_t io = {
        .pin_bit_mask = 1ULL << PIN_BL,
        .mode = GPIO_MODE_OUTPUT
    };
    gpio_config(&io);
    gpio_set_level(PIN_BL, 1);
}

// ======================================================
// MAIN ENTRY
// ======================================================
void app_main(void)
{
    gpio_set_direction(PIN_DC, GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_RST, GPIO_MODE_OUTPUT);

    // ================= LCD SPI =================
    spi_bus_config_t bus_lcd = {
        .mosi_io_num = PIN_MOSI,
        .miso_io_num = -1,
        .sclk_io_num = PIN_CLK,
        .max_transfer_sz = 1024 * 10
    };

    spi_bus_initialize(SPI2_HOST, &bus_lcd, SPI_DMA_CH_AUTO);

    spi_device_interface_config_t dev_lcd = {
        .clock_speed_hz = 20 * 1000 * 1000, // 20MHz
        .mode = 0,
        .spics_io_num = PIN_CS,
        .queue_size = 7,
    };

    spi_bus_add_device(SPI2_HOST, &dev_lcd, &spi_lcd);

    // ================= TOUCH SPI =================
    spi_bus_config_t bus_tp = {
        .mosi_io_num = TP_MOSI,
        .miso_io_num = TP_MISO,
        .sclk_io_num = TP_CLK,
        .max_transfer_sz = 32
    };

    spi_bus_initialize(SPI3_HOST, &bus_tp, SPI_DMA_CH_AUTO);

    spi_device_interface_config_t dev_tp = {
        .clock_speed_hz = 2 * 1000 * 1000,
        .mode = 0,
        .spics_io_num = TP_CS,
        .queue_size = 3,
    };

    spi_bus_add_device(SPI3_HOST, &dev_tp, &spi_tp);

    gpio_set_direction(TP_IRQ, GPIO_MODE_INPUT);

    // ================= LCD INIT =================
    ili9488_init();
    backlight_init();

    // ================= LVGL INIT =================
    lv_init();

    static lv_color_t *buf1;
    buf1 = heap_caps_malloc(LCD_W * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);

    static lv_disp_draw_buf_t draw_buf;
    lv_disp_draw_buf_init(&draw_buf, buf1, NULL, LCD_W * 20);

    // LVGL tick timer (1ms)
    esp_timer_handle_t timer;
    const esp_timer_create_args_t tick_args = {
        .callback = &lv_tick_cb,
        .name = "lv_tick"
    };
    esp_timer_create(&tick_args, &timer);
    esp_timer_start_periodic(timer, 1000); // 1ms

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = LCD_W;
    disp_drv.ver_res = LCD_H;
    disp_drv.flush_cb = lcd_flush;
    disp_drv.draw_buf = &draw_buf;
    disp_drv.full_refresh = 1;

    lv_disp_drv_register(&disp_drv);

    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = touch_read;
    lv_indev_drv_register(&indev_drv);

    ui_create(); // Create LVGL UI

    // Create LVGL task handler in separate task
    xTaskCreate(lvgl_task, "lvgl_task", 4096, NULL, 5, NULL);

    // Keep main task alive
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

// ================= LVGL Task =================
static void lvgl_task(void *arg)
{
    while (1)
    {
        lv_timer_handler(); // LVGL main loop
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐