【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱: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就可以慢慢为我们所用了。