esp32开发与应用(lvgl写定时器)

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

其实有了lvgl可以做很多的事情。就像现在的ipad一样,整个设备最大的一个部分就是屏幕。剩下来的就是相机、按键、wifi、bt、蓝牙、声音这些。而所有的操作,都是在这个屏幕上面完成的。从这个上面来看,可以参见显示的重要性。

1、lvgl开发的步骤

一般来说,lvgl就是绘制界面,然后通过外部事件去改变这个界面。当然,如果想写得简单一点,可以通过定时器来触发,这也是可以的。比如今天写的定时器,就是这么一种应用。

2、lvgl配置的修改

因为是定时器的缘故,所以一些字体要显示得比较大。因此需要通过sdkconfig按钮,重新配置一下lvgl,把font24和font48都打开一下。配置的过程是比较简单的,但是配置完毕之后,就需要整个系统重新编译一下了。这个时间会稍微久一点。

3、修改代码

之前我们有了lvgl成功开发的经验。那么可以在那个代码的基础之上,让ai再写一版定时器的代码,这就相对而言轻松很多了,

复制代码
#include <stdio.h>
#include <string.h>

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

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

#include "lvgl.h"

//================================================
// ST7789
//================================================

#define ST7789_SPI_HOST    SPI2_HOST

#define ST7789_SCLK_PIN    14
#define ST7789_MOSI_PIN    13

#define ST7789_CS_PIN      15
#define ST7789_DC_PIN      2
#define ST7789_RST_PIN     4
#define ST7789_BL_PIN      12

#define ST7789_WIDTH       240
#define ST7789_HEIGHT      240

#define ST7789_SWRESET     0x01
#define ST7789_SLPOUT      0x11
#define ST7789_NORON       0x13
#define ST7789_INVON       0x21
#define ST7789_DISPON      0x29
#define ST7789_CASET       0x2A
#define ST7789_RASET       0x2B
#define ST7789_RAMWR       0x2C
#define ST7789_COLMOD      0x3A
#define ST7789_MADCTL      0x36

static spi_device_handle_t st7789_spi;
static SemaphoreHandle_t lvgl_mutex;

//================================================
// LVGL OBJECTS
//================================================

static lv_obj_t *label_time;
static lv_obj_t *label_date;

//================================================
// SPI
//================================================

static void st7789_send_cmd(uint8_t cmd)
{
    gpio_set_level(ST7789_DC_PIN, 0);

    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd
    };

    spi_device_transmit(st7789_spi, &t);
}

static void st7789_send_data(const uint8_t *data, size_t len)
{
    gpio_set_level(ST7789_DC_PIN, 1);

    spi_transaction_t t = {
        .length = len * 8,
        .tx_buffer = data
    };

    spi_device_transmit(st7789_spi, &t);
}

static void st7789_send_byte(uint8_t data)
{
    gpio_set_level(ST7789_DC_PIN, 1);

    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &data
    };

    spi_device_transmit(st7789_spi, &t);
}

//================================================
// WINDOW
//================================================

static void st7789_set_window(
    uint16_t x0,
    uint16_t y0,
    uint16_t x1,
    uint16_t y1)
{
    st7789_send_cmd(ST7789_CASET);

    uint8_t ca[] = {
        x0 >> 8,
        x0,
        x1 >> 8,
        x1
    };

    st7789_send_data(ca, 4);

    st7789_send_cmd(ST7789_RASET);

    uint8_t ra[] = {
        y0 >> 8,
        y0,
        y1 >> 8,
        y1
    };

    st7789_send_data(ra, 4);

    st7789_send_cmd(ST7789_RAMWR);
}

//================================================
// LCD INIT
//================================================

static void st7789_init(void)
{
    gpio_set_level(ST7789_RST_PIN, 0);
    vTaskDelay(pdMS_TO_TICKS(100));

    gpio_set_level(ST7789_RST_PIN, 1);
    vTaskDelay(pdMS_TO_TICKS(100));

    st7789_send_cmd(ST7789_SWRESET);
    vTaskDelay(pdMS_TO_TICKS(150));

    st7789_send_cmd(ST7789_SLPOUT);
    vTaskDelay(pdMS_TO_TICKS(150));

    st7789_send_cmd(ST7789_COLMOD);
    st7789_send_byte(0x55);

    st7789_send_cmd(ST7789_MADCTL);
    st7789_send_byte(0x00);

    st7789_send_cmd(ST7789_INVON);
    st7789_send_cmd(ST7789_NORON);
    st7789_send_cmd(ST7789_DISPON);

    gpio_set_level(ST7789_BL_PIN, 1);
}

//================================================
// SPI INIT
//================================================

static void st7789_spi_init(void)
{
    gpio_config_t io = {
        .pin_bit_mask =
            (1ULL << ST7789_DC_PIN) |
            (1ULL << ST7789_RST_PIN) |
            (1ULL << ST7789_BL_PIN),
        .mode = GPIO_MODE_OUTPUT
    };

    gpio_config(&io);

    spi_bus_config_t buscfg = {
        .mosi_io_num = ST7789_MOSI_PIN,
        .miso_io_num = -1,
        .sclk_io_num = ST7789_SCLK_PIN,
        .max_transfer_sz =
            ST7789_WIDTH *
            ST7789_HEIGHT *
            2
    };

    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 20000000,
        .mode = 0,
        .spics_io_num = ST7789_CS_PIN,
        .queue_size = 7
    };

    spi_bus_initialize(
        ST7789_SPI_HOST,
        &buscfg,
        SPI_DMA_CH_AUTO);

    spi_bus_add_device(
        ST7789_SPI_HOST,
        &devcfg,
        &st7789_spi);
}

//================================================
// LVGL FLUSH
//================================================

static void st7789_flush_cb(
    lv_disp_drv_t *drv,
    const lv_area_t *area,
    lv_color_t *color_map)
{
    uint32_t w =
        area->x2 - area->x1 + 1;

    uint32_t h =
        area->y2 - area->y1 + 1;

    uint32_t pixels = w * h;

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

    gpio_set_level(ST7789_DC_PIN, 1);

    spi_transaction_t t = {
        .length =
            pixels *
            sizeof(lv_color_t) *
            8,
        .tx_buffer = color_map
    };

    spi_device_transmit(
        st7789_spi,
        &t);

    lv_disp_flush_ready(drv);
}

//================================================
// UI
//================================================

static void create_ui(void)
{
    lv_obj_set_style_bg_color(
        lv_scr_act(),
        lv_color_white(),
        0);

    lv_obj_set_style_bg_opa(
        lv_scr_act(),
        LV_OPA_COVER,
        0);

    label_time =
        lv_label_create(
            lv_scr_act());

    lv_label_set_text(
        label_time,
        "00:00:00");

    lv_obj_set_style_text_color(
        label_time,
        lv_color_hex(0x1565C0),
        0);

    lv_obj_set_style_text_font(
        label_time,
        &lv_font_montserrat_48,
        0);

    lv_obj_align(
        label_time,
        LV_ALIGN_CENTER,
        0,
        -30);

    label_date =
        lv_label_create(
            lv_scr_act());

    lv_label_set_text(
        label_date,
        "2026-06-13");

    lv_obj_set_style_text_color(
        label_date,
        lv_color_hex(0x666666),
        0);

    lv_obj_set_style_text_font(
        label_date,
        &lv_font_montserrat_20,
        0);

    lv_obj_align(
        label_date,
        LV_ALIGN_CENTER,
        0,
        35);
}

//================================================
// LVGL TICK
//================================================

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

//================================================
// CLOCK TASK
//================================================

static void clock_task(void *arg)
{
    int hour = 0;
    int min = 0;
    int sec = 0;

    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));

        sec++;

        if (sec >= 60)
        {
            sec = 0;
            min++;
        }

        if (min >= 60)
        {
            min = 0;
            hour++;
        }

        if (hour >= 24)
        {
            hour = 0;
        }

        char buf[32];

        sprintf(
            buf,
            "%02d:%02d:%02d",
            hour,
            min,
            sec);

        if (xSemaphoreTake(
                lvgl_mutex,
                portMAX_DELAY))
        {
            lv_label_set_text(
                label_time,
                buf);

            xSemaphoreGive(
                lvgl_mutex);
        }
    }
}

//================================================
// APP MAIN
//================================================

void app_main(void)
{
    printf("LVGL CLOCK START\n");

    lvgl_mutex =
        xSemaphoreCreateMutex();

    st7789_spi_init();
    st7789_init();

    lv_init();

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

    static lv_disp_draw_buf_t draw_buf;

    static lv_color_t
        buf1[ST7789_WIDTH * 20];

    static lv_color_t
        buf2[ST7789_WIDTH * 20];

    lv_disp_draw_buf_init(
        &draw_buf,
        buf1,
        buf2,
        ST7789_WIDTH * 20);

    static lv_disp_drv_t disp_drv;

    lv_disp_drv_init(
        &disp_drv);

    disp_drv.hor_res =
        ST7789_WIDTH;

    disp_drv.ver_res =
        ST7789_HEIGHT;

    disp_drv.flush_cb =
        st7789_flush_cb;

    disp_drv.draw_buf =
        &draw_buf;

    lv_disp_drv_register(
        &disp_drv);

    create_ui();

    xTaskCreate(
        clock_task,
        "clock_task",
        4096,
        NULL,
        5,
        NULL);

    while (1)
    {
        lv_timer_handler();
        vTaskDelay(
            pdMS_TO_TICKS(5));
    }
}

4、编译和测试

由于我们有之前lvgl开发的经验,所以这里其实相对而言就简单了。第一,看看代码能不能编译过。第二,下载后效果对不对。第三,如果效果对,看看代码在什么地方做了修改,以后我们在进行类似应用开发的时候,需要怎么做。做到了这些,lvgl就可以慢慢为我们所用了。