#include "lcd_st7701.h" #include "esp_log.h" #include "esp_heap_caps.h" #include "driver/i2c.h" #include "driver/spi_master.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_io_additions.h" #include "esp_lcd_st7701.h" #include "esp_lcd_touch.h" #include "lvgl_port.h" static const char *TAG = "lcd_st7701"; static bool backlight_status = false; #define SCREEN_FACTORY_YY // 鱼鹰屏 // #define SCREEN_FACTORY_HM // 禾木屏 #ifdef SCREEN_FACTORY_HM #include "esp_lcd_touch_gt911.h" #endif #ifdef SCREEN_FACTORY_YY #include "esp_lcd_touch_ft5x06.h" #endif // LCD初始化命令 static const st7701_lcd_init_cmd_t lcd_init_cmds[] = { // {cmd, { data }, data_size, delay_ms} #ifdef SCREEN_FACTORY_HM {0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, {0xC0, (uint8_t[]) {0x3B, 0x00}, 2, 0}, {0xC1, (uint8_t[]) {0x0D, 0x02}, 2, 0}, {0xC2, (uint8_t[]) {0x31, 0x05}, 2, 0}, {0xCD, (uint8_t[]) {0x08}, 1, 0}, {0xB0, (uint8_t[]) {0x00, 0x11, 0x18, 0x0E, 0x11, 0x06, 0x07, 0x08, 0x07, 0x22, 0x04, 0x12, 0x0F, 0xAA, 0x31, 0x18}, 16, 0}, {0xB1, (uint8_t[]) {0x00, 0x11, 0x19, 0x0E, 0x12, 0x07, 0x08, 0x08, 0x08, 0x22, 0x04, 0x11, 0x11, 0xA9, 0x32, 0x18}, 16, 0}, {0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0}, {0xB0, (uint8_t[]) {0x60}, 1, 0}, {0xB1, (uint8_t[]) {0x32}, 1, 0}, {0xB2, (uint8_t[]) {0x07}, 1, 0}, {0xB3, (uint8_t[]) {0x80}, 1, 0}, {0xB5, (uint8_t[]) {0x49}, 1, 0}, {0xB7, (uint8_t[]) {0x85}, 1, 0}, {0xB8, (uint8_t[]) {0x21}, 1, 0}, {0xC1, (uint8_t[]) {0x78}, 1, 0}, {0xC2, (uint8_t[]) {0x78}, 1, 0}, {0xE0, (uint8_t[]) {0x00, 0x1B, 0x02}, 3, 0}, {0xE1, (uint8_t[]) {0x08, 0xA0, 0x00, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x44, 0x44}, 11, 0}, {0xE2, (uint8_t[]) {0x11, 0x11, 0x44, 0x44, 0xED, 0xA0, 0x00, 0x00, 0xEC, 0xA0, 0x00, 0x00}, 12, 0}, {0xE3, (uint8_t[]) {0x00, 0x00, 0x11, 0x11}, 4, 0}, {0xE4, (uint8_t[]) {0x44, 0x44}, 2, 0}, {0xE5, (uint8_t[]) {0x0A, 0xE9, 0xD8, 0xA0, 0x0C, 0xEB, 0xD8, 0xA0, 0x0E, 0xED, 0xD8, 0xA0, 0x10, 0xEF, 0xD8, 0xA0}, 16, 0}, {0xE6, (uint8_t[]) {0x00, 0x00, 0x11, 0x11}, 4, 0}, {0xE7, (uint8_t[]) {0x44, 0x44}, 2, 0}, {0xE8, (uint8_t[]) {0x09, 0xE8, 0xD8, 0xA0, 0x0B, 0xEA, 0xD8, 0xA0, 0x0D, 0xEC, 0xD8, 0xA0, 0x0F, 0xEE, 0xD8, 0xA0}, 16, 0}, {0xEB, (uint8_t[]) {0x02, 0x00, 0xE4, 0xE4, 0x88, 0x00, 0x40}, 7, 0}, {0xEC, (uint8_t[]) {0x3C, 0x00}, 2, 0}, {0xED, (uint8_t[]) {0xAB, 0x89, 0x76, 0x54, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x45, 0x67, 0x98, 0xBA}, 16, 0}, {0x36, (uint8_t[]) {0x00}, 1, 0}, {0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0}, {0xE5, (uint8_t[]) {0xE4}, 1, 0}, {0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0}, {0x3A, (uint8_t[]) {0x60}, 1, 0}, {0x20, (uint8_t[]) {}, 0, 0}, {0x11, (uint8_t[]) {}, 0, 100}, {0x29, (uint8_t[]) {}, 0, 50} #endif #ifdef SCREEN_FACTORY_YY {0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0}, {0xEF, (uint8_t[]) {0x08}, 1, 0}, {0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, {0xC0, (uint8_t[]) {0x3B, 0x00}, 2, 0}, {0xC1, (uint8_t[]) {0x0B, 0x02}, 2, 0}, {0xC2, (uint8_t[]) {0x37, 0x02}, 2, 0}, {0xCC, (uint8_t[]) {0x10}, 1, 0}, {0xB0, (uint8_t[]) {0x00, 0x0F, 0x16, 0x0E, 0x11, 0x07, 0x09, 0x09, 0x08, 0x23, 0x05, 0x11, 0x0F, 0x28, 0x2D, 0x18}, 16, 0}, {0xB1, (uint8_t[]) {0x00, 0x0F, 0x16, 0x0E, 0x11, 0x07, 0x09, 0x08, 0x09, 0x23, 0x05, 0x11, 0x0F, 0x28, 0x2D, 0x18}, 16, 0}, {0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0}, {0xB0, (uint8_t[]) {0x4D}, 1, 0}, {0xB1, (uint8_t[]) {0x33}, 1, 0}, {0xB2, (uint8_t[]) {0x87}, 1, 0}, {0xB5, (uint8_t[]) {0x4B}, 1, 0}, {0xB7, (uint8_t[]) {0x8C}, 1, 0}, {0xB8, (uint8_t[]) {0x20}, 1, 0}, {0xC1, (uint8_t[]) {0x78}, 1, 0}, {0xC2, (uint8_t[]) {0x78}, 1, 0}, {0xD0, (uint8_t[]) {0x88}, 1, 0}, {0xE0, (uint8_t[]) {0x00, 0x00, 0x02}, 3, 0}, {0xE1, (uint8_t[]) {0x02, 0xF0, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x44, 0x44}, 11, 0}, {0xE2, (uint8_t[]) {0x10, 0x10, 0x40, 0x40, 0xF2, 0xF0, 0x00, 0x00, 0xF2, 0xF0, 0x00, 0x00}, 12, 0}, {0xE3, (uint8_t[]) {0x00, 0x00, 0x11, 0x11}, 4, 0}, {0xE4, (uint8_t[]) {0x44, 0x44}, 2, 0}, {0xE5, (uint8_t[]) {0x07, 0xEF, 0xF0, 0xF0, 0x09, 0xF1, 0xF0, 0xF0, 0x03, 0xF3, 0xF0, 0xF0, 0x05, 0xED, 0xF0, 0xF0}, 16, 0}, {0xE6, (uint8_t[]) {0x00, 0x00, 0x11, 0x11}, 4, 0}, {0xE7, (uint8_t[]) {0x44, 0x44}, 2, 0}, {0xE8, (uint8_t[]) {0x08, 0xF0, 0xF0, 0xF0, 0x0A, 0xF2, 0xF0, 0xF0, 0x04, 0xF4, 0xF0, 0xF0, 0x06, 0xEE, 0xF0, 0xF0}, 16, 0}, {0xEB, (uint8_t[]) {0x00, 0x00, 0xE4, 0xE4, 0x44, 0x88, 0x40}, 7, 0}, {0xEC, (uint8_t[]) {0x78, 0x00}, 2, 0}, {0xED, (uint8_t[]) {0x20, 0xF9, 0x87, 0x76, 0x65, 0x54, 0x4F, 0xFF, 0xFF, 0xF4, 0x45, 0x56, 0x67, 0x78, 0x9F, 0x02}, 16, 0}, {0xEF, (uint8_t[]) {0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0}, // 退出睡眠模式和开启显示的命令保持不变 {0x11, (uint8_t[]) {0x00}, 0, 120}, // Sleep Out {0x29, (uint8_t[]) {0x00}, 0, 0}, // Display On #endif }; static void spi_soft_init(void) { gpio_config_t io_conf1 = { .mode = GPIO_MODE_OUTPUT, .pin_bit_mask = 1ULL << LCD_IO_SPI_CS, }; gpio_config(&io_conf1); gpio_set_level(LCD_IO_SPI_CS, 1); gpio_config_t io_conf2 = { .mode = GPIO_MODE_OUTPUT, .pin_bit_mask = 1ULL << LCD_IO_SPI_SCL, }; gpio_config(&io_conf2); gpio_set_level(LCD_IO_SPI_SCL, 1); gpio_config_t io_conf3 = { .mode = GPIO_MODE_OUTPUT, .pin_bit_mask = 1ULL << LCD_IO_SPI_SDA, }; gpio_config(&io_conf3); gpio_set_level(LCD_IO_SPI_SDA, 1); } static void spi_soft_write_9bits(uint16_t data) { uint8_t i; LCD_CS_Clr(); for (i = 0; i < 9; i++) { LCD_SCK_Clr(); if (data & 0x100) LCD_SDA_Set(); else LCD_SDA_Clr(); LCD_SCK_Set(); data <<= 1; } LCD_CS_Set();; } static void st7701_write_cmd(uint8_t cmd) { uint16_t temp = 0; temp = temp | cmd; spi_soft_write_9bits(temp); } static void st7701_write_data(uint8_t data) { uint16_t temp = 0x100; temp = temp | data; spi_soft_write_9bits(temp); } IRAM_ATTR static bool rgb_lcd_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) { // 最小化中断处理时间 return lvgl_port_notify_rgb_vsync(); } esp_err_t lcd_st7701_init(void) { // 配置背光GPIO if (PIN_NUM_BK_LIGHT >= 0) { gpio_config_t bk_gpio_config = { .mode = GPIO_MODE_OUTPUT, .pin_bit_mask = 1ULL << PIN_NUM_BK_LIGHT}; ESP_ERROR_CHECK(gpio_config(&bk_gpio_config)); } // 安装3线SPI面板IO ESP_LOGI(TAG, "Install 3-wire SPI panel IO"); spi_line_config_t line_config = { .cs_io_type = IO_TYPE_GPIO, .cs_gpio_num = LCD_IO_SPI_CS, .scl_io_type = IO_TYPE_GPIO, .scl_gpio_num = LCD_IO_SPI_SCL, .sda_io_type = IO_TYPE_GPIO, .sda_gpio_num = LCD_IO_SPI_SDA, .io_expander = NULL, }; esp_lcd_panel_io_3wire_spi_config_t io_config = ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0); esp_lcd_panel_io_handle_t io_handle = NULL; ESP_ERROR_CHECK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle)); for (int i = 0; i < sizeof(lcd_init_cmds) / sizeof(lcd_init_cmds[0]); i++) { st7701_write_cmd(lcd_init_cmds[i].cmd); for (int n = 0; n < lcd_init_cmds[i].data_bytes; n++) { st7701_write_data(((uint8_t *) lcd_init_cmds[i].data)[n]); } vTaskDelay(pdMS_TO_TICKS(lcd_init_cmds[i].delay_ms)); } // 安装RGB面板 ESP_LOGI(TAG, "Install ST7701 panel driver"); esp_lcd_panel_handle_t lcd_handle = NULL; esp_lcd_rgb_panel_config_t rgb_config = { .clk_src = LCD_CLK_SRC_DEFAULT, .psram_trans_align = 64, .data_width = RGB_DATA_WIDTH, .bits_per_pixel = RGB_BIT_PER_PIXEL, .de_gpio_num = LCD_IO_RGB_DE, .pclk_gpio_num = LCD_IO_RGB_PCLK, .vsync_gpio_num = LCD_IO_RGB_VSYNC, .hsync_gpio_num = LCD_IO_RGB_HSYNC, .disp_gpio_num = LCD_IO_RGB_DISP, .data_gpio_nums = { LCD_IO_RGB_DATA0, LCD_IO_RGB_DATA1, LCD_IO_RGB_DATA2, LCD_IO_RGB_DATA3, LCD_IO_RGB_DATA4, LCD_IO_RGB_DATA5, LCD_IO_RGB_DATA6, LCD_IO_RGB_DATA7, LCD_IO_RGB_DATA8, LCD_IO_RGB_DATA9, LCD_IO_RGB_DATA10, LCD_IO_RGB_DATA11, LCD_IO_RGB_DATA12, LCD_IO_RGB_DATA13, LCD_IO_RGB_DATA14, LCD_IO_RGB_DATA15, }, .timings = ST7701_480_480_PANEL_60HZ_RGB_TIMING(), .flags.fb_in_psram = 1, .num_fbs = LVGL_PORT_LCD_RGB_BUFFER_NUMS, .bounce_buffer_size_px = RGB_BOUNCE_BUFFER_SIZE, }; rgb_config.timings.h_res = LCD_H_RES; rgb_config.timings.v_res = LCD_V_RES; st7701_vendor_config_t vendor_config = { .rgb_config = &rgb_config, .init_cmds = lcd_init_cmds, .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(lcd_init_cmds[0]), .flags = { .auto_del_panel_io = 0, .mirror_by_cmd = 1, }, }; const esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = LCD_IO_RST, .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, .bits_per_pixel = LCD_BIT_PER_PIXEL, .vendor_config = &vendor_config, }; ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &lcd_handle)); ESP_ERROR_CHECK(esp_lcd_panel_reset(lcd_handle)); ESP_ERROR_CHECK(esp_lcd_panel_init(lcd_handle)); esp_lcd_panel_disp_on_off(lcd_handle, true); // 初始化I2C ESP_LOGI(TAG, "Initialize I2C"); const i2c_config_t i2c_conf = { .mode = I2C_MODE_MASTER, .sda_io_num = PIN_NUM_TOUCH_SDA, .scl_io_num = PIN_NUM_TOUCH_SCL, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = 400000, }; ESP_ERROR_CHECK(i2c_param_config(TOUCH_HOST, &i2c_conf)); ESP_ERROR_CHECK(i2c_driver_install(TOUCH_HOST, i2c_conf.mode, 0, 0, 0)); // 初始化触摸控制器 #ifdef SCREEN_FACTORY_HM ESP_LOGI(TAG, "Initialize touch controller GT911"); esp_lcd_panel_io_handle_t tp_io_handle = NULL; esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t) TOUCH_HOST, &tp_io_config, &tp_io_handle)); #endif #ifdef SCREEN_FACTORY_YY ESP_LOGI(TAG, "Initialize touch controller FT5x06"); esp_lcd_panel_io_handle_t tp_io_handle = NULL; esp_lcd_panel_io_i2c_config_t tp_io_config = { .dev_addr = ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS, .control_phase_bytes = 1, .dc_bit_offset = 0, .lcd_cmd_bits = 8, .flags = { .disable_control_phase = 1, }}; ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t) TOUCH_HOST, &tp_io_config, &tp_io_handle)); #endif esp_lcd_touch_config_t tp_cfg = { .x_max = LCD_H_RES, .y_max = LCD_V_RES, .rst_gpio_num = PIN_NUM_TOUCH_RST, .int_gpio_num = PIN_NUM_TOUCH_INT, .levels = { .reset = 0, .interrupt = 0, }, .flags = { .swap_xy = 0, .mirror_x = 0, .mirror_y = 0, }, }; ESP_LOGI(TAG, "Create touch handle"); esp_lcd_touch_handle_t tp = NULL; #ifdef SCREEN_FACTORY_HM ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp)); #endif #ifdef SCREEN_FACTORY_YY ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &tp)); #endif assert(tp != NULL); // 初始化LVGL ESP_ERROR_CHECK(lvgl_port_init(lcd_handle, tp)); // 注册RGB面板事件回调 esp_lcd_rgb_panel_event_callbacks_t cbs = { #if RGB_BOUNCE_BUFFER_SIZE > 0 .on_bounce_frame_finish = rgb_lcd_on_vsync_event, #else .on_vsync = rgb_lcd_on_vsync_event, #endif }; esp_lcd_rgb_panel_register_event_callbacks(lcd_handle, &cbs, NULL); return ESP_OK; } esp_err_t lcd_st7701_backlight_on(void) { if (PIN_NUM_BK_LIGHT >= 0) { st7701_write_cmd(0x11); vTaskDelay(120); st7701_write_cmd(0x29); ESP_LOGI(TAG, "Turn on LCD backlight"); backlight_status = true; return gpio_set_level(PIN_NUM_BK_LIGHT, LCD_BK_LIGHT_ON_LEVEL); } return ESP_OK; } bool lcd_st7701_get_blacklight() { return backlight_status; } esp_err_t lcd_st7701_backlight_off(void) { if (PIN_NUM_BK_LIGHT >= 0) { st7701_write_cmd(0x10); vTaskDelay(120); st7701_write_cmd(0x28); ESP_LOGI(TAG, "Turn off LCD backlight"); backlight_status = false; return gpio_set_level(PIN_NUM_BK_LIGHT, LCD_BK_LIGHT_OFF_LEVEL); } return ESP_OK; }