#include "lcd_driver.h" #include "esp_log.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_ops.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" static const char *TAG = "LCD_DRIVER"; // ============ ST7789 初始化序列 ============ static const uint8_t st7789_init_cmds[] = { 0x11, 0, 120, // SLPOUT - 退出睡眠模式 0x3A, 1, 0, 0x55, // COLMOD - 像素格式: 16bit 0x36, 1, 0, 0x00, // MADCTL - 内存数据访问控制 0xB2, 5, 0, 0x0C, 0x0C, 0x00, 0x33, 0x33, // PORCTRL - 帧率控制 0xB7, 1, 0, 0x35, // GCTRL - 门控制 0xBB, 1, 0, 0x19, // VCOMS - VCOM电压 0xC0, 1, 0, 0x2C, // LCMCTRL - LCD控制 0xC2, 2, 0, 0x01, 0xFF, // VDVVRHEN - VDV和VRH命令使能 0xC3, 1, 0, 0x11, // VRHSET - VRH设置 0xC4, 1, 0, 0x20, // VDVS - VDV设置 0xC6, 1, 0, 0x0F, // FRCTRL2 - 帧率控制2 0xD0, 2, 0, 0xA4, 0xA1, // PWCTRL1 - 电源控制1 0xE0, 14, 0, 0xD0, 0x04, 0x0D, 0x11, 0x13, 0x2B, 0x3F, 0x54, 0x4C, 0x18, 0x0D, 0x0B, 0x1F, 0x23, // PVGAMCTRL - 正伽马 0xE1, 14, 0, 0xD0, 0x04, 0x0C, 0x11, 0x13, 0x2C, 0x3F, 0x44, 0x51, 0x2F, 0x1F, 0x1F, 0x20, 0x23, // NVGAMCTRL - 负伽马 0x21, 0, 0, // INVON - 显示反转开启 0x29, 0, 120, // DISPON - 显示开启 }; // ============ 初始化SPI总线 ============ static esp_err_t init_spi_bus(spi_host_device_t host, gpio_num_t mosi, gpio_num_t sclk) { spi_bus_config_t bus_cfg = { .mosi_io_num = mosi, .miso_io_num = -1, // LCD通常不需要MISO .sclk_io_num = sclk, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = LCD_H_RES * LCD_BIT_PER_PIXEL / 8 * 8, .flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_GPIO_PINS, }; esp_err_t ret = spi_bus_initialize(host, &bus_cfg, SPI_DMA_CH_AUTO); if (ret != ESP_OK) { ESP_LOGE(TAG, "SPI总线初始化失败: %s", esp_err_to_name(ret)); } return ret; } // ============ 初始化SPI设备 ============ static esp_err_t init_spi_device(spi_host_device_t host, gpio_num_t cs, spi_device_handle_t *spi_handle) { spi_device_interface_config_t dev_cfg = { .clock_speed_hz = LCD_SPI_SPEED_HZ, // 80MHz .mode = 0, // SPI Mode 0 .spics_io_num = cs, .queue_size = 7, .flags = SPI_DEVICE_NO_DUMMY, .pre_cb = NULL, .post_cb = NULL, }; esp_err_t ret = spi_bus_add_device(host, &dev_cfg, spi_handle); if (ret != ESP_OK) { ESP_LOGE(TAG, "SPI设备添加失败: %s", esp_err_to_name(ret)); } return ret; } // ============ 创建LCD面板IO ============ static esp_err_t create_lcd_panel_io(spi_device_handle_t spi_handle, gpio_num_t dc_gpio, esp_lcd_panel_io_handle_t *io_handle) { esp_lcd_panel_io_spi_config_t io_config = { .dc_gpio_num = dc_gpio, .cs_gpio_num = -1, // 已在SPI设备中配置 .pclk_hz = LCD_SPI_SPEED_HZ, .trans_queue_depth = 10, .lcd_cmd_bits = 8, .lcd_param_bits = 8, .spi_mode = 0, .flags = { .dc_low_on_data = 0, .octal_mode = 0, .sio_mode = 0, .lsb_first = 0, .cs_high_active = 0, }, }; esp_err_t ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)spi_handle, &io_config, io_handle); if (ret != ESP_OK) { ESP_LOGE(TAG, "创建LCD面板IO失败: %s", esp_err_to_name(ret)); } return ret; } // ============ 创建LCD面板 ============ static esp_err_t create_lcd_panel(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t *panel_handle, lcd_type_t type) { esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = -1, // 手动控制复位 .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, .bits_per_pixel = LCD_BIT_PER_PIXEL, }; esp_err_t ret; switch (type) { case LCD_TYPE_ST7789: ret = esp_lcd_new_panel_st7789(io_handle, &panel_config, panel_handle); break; case LCD_TYPE_ILI9341: ret = esp_lcd_new_panel_ili9341(io_handle, &panel_config, panel_handle); break; case LCD_TYPE_GC9A01: ret = esp_lcd_new_panel_st7789(io_handle, &panel_config, panel_handle); break; default: ret = ESP_ERR_INVALID_ARG; } if (ret != ESP_OK) { ESP_LOGE(TAG, "创建LCD面板失败: %s", esp_err_to_name(ret)); } return ret; } // ============ 驱动初始化 ============ esp_err_t lcd_driver_init(lcd_driver_t *lcd, lcd_type_t type) { esp_err_t ret; // 初始化GPIO gpio_config_t io_conf = { .pin_bit_mask = (1ULL << lcd->dc_gpio) | (1ULL << lcd->rst_gpio) | (1ULL << lcd->bckl_gpio), .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, }; gpio_config(&io_conf); // 复位LCD gpio_set_level(lcd->rst_gpio, 0); vTaskDelay(pdMS_TO_TICKS(10)); gpio_set_level(lcd->rst_gpio, 1); vTaskDelay(pdMS_TO_TICKS(120)); // 初始化SPI总线 ret = init_spi_bus(lcd->spi_handle ? LCD_SPI_HOST : LCD_SPI_HOST, LCD_PIN_NUM_MOSI, LCD_PIN_NUM_SCLK); if (ret != ESP_OK) return ret; // 初始化SPI设备 spi_device_interface_config_t dev_cfg = { .clock_speed_hz = LCD_SPI_SPEED_HZ, .mode = 0, .spics_io_num = lcd->cs_gpio, .queue_size = 7, .flags = SPI_DEVICE_NO_DUMMY, }; ret = spi_bus_add_device(LCD_SPI_HOST, &dev_cfg, &lcd->spi_handle); if (ret != ESP_OK) return ret; // 创建面板IO esp_lcd_panel_io_handle_t io_handle = NULL; ret = create_lcd_panel_io(lcd->spi_handle, lcd->dc_gpio, &io_handle); if (ret != ESP_OK) return ret; // 创建面板 ret = create_lcd_panel(io_handle, &lcd->panel_handle, type); if (ret != ESP_OK) return ret; // 初始化面板 ret = esp_lcd_panel_reset(lcd->panel_handle); if (ret != ESP_OK) { ESP_LOGW(TAG, "面板复位失败,继续执行..."); } ret = esp_lcd_panel_init(lcd->panel_handle); if (ret != ESP_OK) { ESP_LOGE(TAG, "面板初始化失败: %s", esp_err_to_name(ret)); return ret; } // 关闭睡眠模式 esp_lcd_panel_disp_on_off(lcd->panel_handle, true); // 设置默认背光 lcd_set_backlight(lcd, 100); ESP_LOGI(TAG, "LCD初始化完成,SPI速度: %d MHz", LCD_SPI_SPEED_HZ / 1000000); return ESP_OK; } // ============ 驱动反初始化 ============ esp_err_t lcd_driver_deinit(lcd_driver_t *lcd) { if (lcd->panel_handle) { esp_lcd_panel_del(lcd->panel_handle); lcd->panel_handle = NULL; } if (lcd->spi_handle) { spi_bus_remove_device(lcd->spi_handle); lcd->spi_handle = NULL; } spi_bus_free(LCD_SPI_HOST); ESP_LOGI(TAG, "LCD驱动已释放"); return ESP_OK; } // ============ 绘制单个像素 ============ esp_err_t lcd_draw_pixel(lcd_driver_t *lcd, uint16_t x, uint16_t y, uint16_t color) { if (x >= LCD_H_RES || y >= LCD_V_RES) { return ESP_ERR_INVALID_ARG; } esp_lcd_panel_set_window(lcd->panel_handle, x, y, x, y); uint16_t data = color; esp_lcd_panel_draw_bitmap(lcd->panel_handle, x, y, x + 1, y + 1, &data); return ESP_OK; } // ============ 填充矩形 ============ esp_err_t lcd_fill_rect(lcd_driver_t *lcd, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { if (x >= LCD_H_RES || y >= LCD_V_RES) { return ESP_ERR_INVALID_ARG; } // 限制宽高 if (x + w > LCD_H_RES) w = LCD_H_RES - x; if (y + h > LCD_V_RES) h = LCD_V_RES - y; uint16_t *buf = heap_caps_malloc(w * h * sizeof(uint16_t), MALLOC_CAP_DMA); if (!buf) { ESP_LOGE(TAG, "内存分配失败"); return ESP_ERR_NO_MEM; } // 填充缓冲区 for (int i = 0; i < w * h; i++) { buf[i] = color; } esp_lcd_panel_set_window(lcd->panel_handle, x, y, x + w - 1, y + h - 1); esp_lcd_panel_draw_bitmap(lcd->panel_handle, x, y, x + w, y + h, buf); heap_caps_free(buf); return ESP_OK; } // ============ 绘制图像 ============ esp_err_t lcd_draw_image(lcd_driver_t *lcd, uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data) { if (x >= LCD_H_RES || y >= LCD_V_RES || !data) { return ESP_ERR_INVALID_ARG; } if (x + w > LCD_H_RES) w = LCD_H_RES - x; if (y + h > LCD_V_RES) h = LCD_V_RES - y; esp_lcd_panel_set_window(lcd->panel_handle, x, y, x + w - 1, y + h - 1); esp_lcd_panel_draw_bitmap(lcd->panel_handle, x, y, x + w, y + h, data); return ESP_OK; } // ============ 设置旋转 ============ esp_err_t lcd_set_rotation(lcd_driver_t *lcd, uint8_t rotation) { return esp_lcd_panel_set_gap(lcd->panel_handle, 0, 0); } // ============ 设置背光 ============ esp_err_t lcd_set_backlight(lcd_driver_t *lcd, uint8_t brightness) { if (brightness > 100) brightness = 100; // 使用LED PWM控制背光 // 注意:ESP32-C3使用LEDC需要不同配置 #if CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3的PWM配置 #endif gpio_set_level(lcd->bckl_gpio, (brightness > 0) ? 1 : 0); return ESP_OK; } // ============ 清屏 ============ esp_err_t lcd_clear(lcd_driver_t *lcd, uint16_t color) { return lcd_fill_rect(lcd, 0, 0, LCD_H_RES, LCD_V_RES, color); }