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

        前面我们学习了lvgl,也学习了怎么用lvgl写一个定时器。但是由于之前的屏幕分辨率不高,所以我们想调整一下,变成320*240。但这个时候,驱动芯片也发生了变化。之前是st7789,现在是ili9341,但是因为有ai,整个过程不复杂。

1、连线基本不变

        不管是哪个驱动ic,接线部分基本还是spi接线,只是线序可能有差别。其他都是一样的。所以这部分不用担心。

2、主动告诉ai,换了驱动ic

        我们在让ai写驱动代码的时候,需要主动告诉它,当前新的驱动ic是什么,分辨率是多少,默认哪个是长、那个是宽,这些都是需要提前说好的。不然生成的代码有可能是错的。

3、间隙性确认ai的代码

        ai生成的代码,不保证每一步都是对的。这个时候一方面,我们要对代码进行保存;另外一方面需要把代码发给ai,告诉它下一个功能在这个基础上修改。这样基本上可以减少很多无谓的沟通。

4、发送需求给ai

        到这里,我们应该知道如何发送需求给ai了,即在当前st7789 demo的基础之上,替换成ili9341 ic,分辨率是240*320,demo需要旋转过来,改动尽量最小。不出意外,就可以拿到这样的代码,

#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 "lvgl.h"

//================================================
// ILI9341 CONFIG
//================================================
#define ILI9341_SPI_HOST    SPI2_HOST
#define ILI9341_SCLK_PIN    14
#define ILI9341_MOSI_PIN    13
#define ILI9341_CS_PIN      15
#define ILI9341_DC_PIN      2
#define ILI9341_RST_PIN     4
#define ILI9341_BL_PIN      12

#define ILI9341_WIDTH       320
#define ILI9341_HEIGHT      240

#define ILI9341_SWRESET     0x01
#define ILI9341_SLPOUT      0x11
#define ILI9341_NORON       0x13
#define ILI9341_INVOFF      0x20
#define ILI9341_DISPON      0x29
#define ILI9341_CASET       0x2A
#define ILI9341_RASET       0x2B
#define ILI9341_RAMWR       0x2C
#define ILI9341_COLMOD      0x3A
#define ILI9341_MADCTL      0x36

static spi_device_handle_t ili9341_spi;
static SemaphoreHandle_t lvgl_mutex;
static lv_obj_t *label_time;
static lv_obj_t *label_date;

//================================================
// SPI LOW LEVEL
//================================================
static void ili9341_send_cmd(uint8_t cmd)
{
    gpio_set_level(ILI9341_DC_PIN, 0);
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd
    };
    spi_device_transmit(ili9341_spi, &t);
}

static void ili9341_send_data(const uint8_t *data, size_t len)
{
    gpio_set_level(ILI9341_DC_PIN, 1);
    spi_transaction_t t = {
        .length = len * 8,
        .tx_buffer = data
    };
    spi_device_transmit(ili9341_spi, &t);
}

//================================================
// WINDOW
//================================================
static void ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
    ili9341_send_cmd(ILI9341_CASET);
    uint8_t ca[] = { x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF };
    ili9341_send_data(ca, 4);

    ili9341_send_cmd(ILI9341_RASET);
    uint8_t ra[] = { y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF };
    ili9341_send_data(ra, 4);

    ili9341_send_cmd(ILI9341_RAMWR);
}

//================================================
// LCD INIT
//================================================
static void ili9341_init(void)
{
    gpio_set_level(ILI9341_RST_PIN, 0);
    vTaskDelay(pdMS_TO_TICKS(100));
    gpio_set_level(ILI9341_RST_PIN, 1);
    vTaskDelay(pdMS_TO_TICKS(120));

    ili9341_send_cmd(ILI9341_SWRESET);
    vTaskDelay(pdMS_TO_TICKS(120));

    ili9341_send_cmd(ILI9341_SLPOUT);
    vTaskDelay(pdMS_TO_TICKS(120));

    ili9341_send_cmd(ILI9341_COLMOD);
    ili9341_send_data((uint8_t[]){0x55}, 1);

    ili9341_send_cmd(ILI9341_MADCTL);
    ili9341_send_data((uint8_t[]){0x28}, 1);

    ili9341_send_cmd(ILI9341_DISPON);

    gpio_set_level(ILI9341_BL_PIN, 1);
}

//================================================
// SPI INIT
//================================================
static void ili9341_spi_init(void)
{
    gpio_config_t io = {
        .pin_bit_mask =
            (1ULL << ILI9341_DC_PIN) |
            (1ULL << ILI9341_RST_PIN) |
            (1ULL << ILI9341_BL_PIN),
        .mode = GPIO_MODE_OUTPUT
    };
    gpio_config(&io);

    spi_bus_config_t buscfg = {
        .mosi_io_num = ILI9341_MOSI_PIN,
        .miso_io_num = -1,
        .sclk_io_num = ILI9341_SCLK_PIN,
        .max_transfer_sz = ILI9341_WIDTH * ILI9341_HEIGHT * 2
    };

    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 20 * 1000 * 1000,
        .mode = 0,
        .spics_io_num = ILI9341_CS_PIN,
        .queue_size = 7
    };

    spi_bus_initialize(ILI9341_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
    spi_bus_add_device(ILI9341_SPI_HOST, &devcfg, &ili9341_spi);
}

//================================================
// LVGL FLUSH
//================================================
static void ili9341_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;

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

    gpio_set_level(ILI9341_DC_PIN, 1);

    spi_transaction_t t = {
        .length = pixels * 16,
        .tx_buffer = color_map
    };

    spi_device_transmit(ili9341_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(0xff0000), // brg
                                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-17");

    lv_obj_set_style_text_color(label_date,
                                lv_color_hex(0x00ff00), // brg
                                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 h = 0, m = 0, s = 0;
    char buf[32];

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

        s++;
        if (s >= 60) { s = 0; m++; }
        if (m >= 60) { m = 0; h++; }
        if (h >= 24) { h = 0; }

        sprintf(buf, "%02d:%02d:%02d", h, m, s);

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

//================================================
// MAIN
//================================================
void app_main(void)
{
    printf("ILI9341 START\n");

    lvgl_mutex = xSemaphoreCreateMutex();

    ili9341_spi_init();
    ili9341_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[ILI9341_WIDTH * 20];
    static lv_color_t buf2[ILI9341_WIDTH * 20];

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

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);

    disp_drv.hor_res = ILI9341_WIDTH;
    disp_drv.ver_res = ILI9341_HEIGHT;
    disp_drv.flush_cb = ili9341_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));
    }
}

5、编译、测试和验证

        这部分和之前流程差不多,先编译、再烧入和测试。代码功能差不多了,再去慢慢看细节。要是代码本身都不能编译和烧录,或者运行是错的,那就意义不大。

注:

        有一些屏幕虽然小一点,比如是2.4寸屏,但是分辨率也是320*240。所以这份代码可以直接port过去使用,没有问题,只要屏幕驱动芯片也是ILI9341。

Logo

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

更多推荐