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