esp32开发与应用(继续升级到3.5寸屏幕)

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

前面我们已经升级了一次屏幕,当时是从240*240,升级到320*240。在这过程中,屏幕驱动ic也发生了改变。这一次,我们继续升级屏幕,升级到480*320,驱动芯片从ILI9341调整为ILI9488。对于mcu应用来说,480*320的分辨率足以覆盖大部分场景了。

1、总线部分

驱动芯片虽然发生了变化,但是使用的还是spi总线,这部分不变。

2、连接线

实际连线的时候,除了spi的三根线(去除miso),还有三根线,即dc、rst、bl。这和之前也是一样的,没有发生改变。

3、查找代码时的注意事项

这一次用ai查找代码的时候,本来想一步到位,希望ai直接生成基于lvgl的ili9488驱动代码。实际操作下来,错误很多。一会是lvgl的问题,一会是9488的问题。所以,最后还是从基础开始,直接用ai生成裸屏驱动代码,这才错误慢慢收敛起来。

4、用ai生成裸屏驱动代码

这里生成驱动代码时候,有一些细节是需要高速ai的,比如用esp32驱动,驱动芯片是ili9488,分辨率是480*320,希望生成宽屏代码。这样不出意外,就可以得到下面这些内容,

复制代码
#include <stdio.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"

// ================= 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

static spi_device_handle_t spi;
static const char *TAG = "ILI9488_AUTO";

// ================= LCD MODE =================
typedef enum {
    MODE_RGB565 = 0,
    MODE_RGB666 = 1,
} lcd_mode_t;

static lcd_mode_t lcd_mode = MODE_RGB565;

// ================= GPIO =================
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 =================
static void lcd_cmd(uint8_t cmd)
{
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd,
    };
    dc_cmd();
    spi_device_transmit(spi, &t);
}

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

// ================= INIT =================
static void ili9488_init(lcd_mode_t mode)
{
    lcd_reset();

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

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

    lcd_cmd(0x3A);  // Pixel format
    uint8_t pix = (mode == MODE_RGB565) ? 0x55 : 0x66;
    lcd_data(&pix, 1);

    lcd_cmd(0x36);  // Memory access control
    uint8_t mad = 0x28;
    lcd_data(&mad, 1);

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

    lcd_mode = mode;

    ESP_LOGI(TAG, "init mode = %s",
             mode == MODE_RGB565 ? "RGB565" : "RGB666");
}

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

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

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

    lcd_cmd(0x2C);  // Memory write
}

// ================= AUTO SAFE FILL =================
#define BUF_PIXELS 1024

static uint8_t buf[BUF_PIXELS * 3]; // Maximum size prepared for RGB666

static void fill_color(uint16_t color)
{
    set_window(0,0,LCD_W-1,LCD_H-1);

    if (lcd_mode == MODE_RGB565) {

        uint8_t hi = color >> 8;
        uint8_t lo = color & 0xFF;

        for (int i=0;i<BUF_PIXELS;i++) {
            buf[i*2] = hi;
            buf[i*2+1] = lo;
        }

        spi_transaction_t t = {
            .length = BUF_PIXELS * 16,
            .tx_buffer = buf,
        };

        int blocks = (LCD_W*LCD_H)/BUF_PIXELS;
        for (int i=0;i<blocks;i++) {
            dc_data();
            spi_device_transmit(spi,&t);
        }

    } else {

        uint8_t r = (color >> 11) & 0x1F;
        uint8_t g = (color >> 5)  & 0x3F;
        uint8_t b = color & 0x1F;

        r <<= 3;
        g <<= 2;
        b <<= 3;

        for (int i=0;i<BUF_PIXELS;i++) {
            buf[i*3+0]=r;
            buf[i*3+1]=g;
            buf[i*3+2]=b;
        }

        spi_transaction_t t = {
            .length = BUF_PIXELS * 24,
            .tx_buffer = buf,
        };

        int blocks = (LCD_W*LCD_H)/BUF_PIXELS;
        for (int i=0;i<blocks;i++) {
            dc_data();
            spi_device_transmit(spi,&t);
        }
    }
}

// ================= BACKLIGHT =================
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 =================
void app_main(void)
{
    gpio_set_direction(PIN_DC,GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_RST,GPIO_MODE_OUTPUT);

    backlight_init();

    spi_bus_config_t bus={
        .mosi_io_num=PIN_MOSI,
        .miso_io_num=-1,
        .sclk_io_num=PIN_CLK,
        .max_transfer_sz=1024*3
    };

    spi_bus_initialize(SPI2_HOST,&bus,SPI_DMA_CH_AUTO);

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

    spi_bus_add_device(SPI2_HOST,&dev,&spi);

    // ⭐ Auto mode: try RGB565 first, switch to RGB666 if it doesn't work
    ili9488_init(MODE_RGB666);

    while (1) {

        ESP_LOGI(TAG, "COLOR TEST -> RED   (0xF800)");
        fill_color(0xF800);
        vTaskDelay(pdMS_TO_TICKS(500));

        ESP_LOGI(TAG, "COLOR TEST -> GREEN (0x07E0)");
        fill_color(0x07E0);
        vTaskDelay(pdMS_TO_TICKS(500));

        ESP_LOGI(TAG, "COLOR TEST -> BLUE  (0x001F)");
        fill_color(0x001F);
        vTaskDelay(pdMS_TO_TICKS(500));

        ESP_LOGI(TAG, "COLOR TEST -> WHITE (0xFFFF)");
        fill_color(0xFFFF);
        vTaskDelay(pdMS_TO_TICKS(500));

        ESP_LOGI(TAG, "COLOR TEST -> BLACK (0x0000)");
        fill_color(0x0000);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

5、编译和测试

编译和测试这部分,和之前一样。先保证代码是对的,能运行的。然后再去慢慢理解,这样比较明智。**但是比较难的地方,是遇到问题的时候,如何和ai进行交互,这部分其实还是比较吃经验的。**指望从来没有驱动经验的同学,可以短时间快速驱动好屏幕,着实有点困难。