【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱: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进行交互,这部分其实还是比较吃经验的。**指望从来没有驱动经验的同学,可以短时间快速驱动好屏幕,着实有点困难。