#include "display/display.h" #include "common/constants.h" #include "esp_log.h" DisplayHandler::~DisplayHandler() { if (_spi_mutex != nullptr) { vSemaphoreDelete(_spi_mutex); } if (_spi != nullptr) { spi_bus_remove_device(_spi); } if (_tp_handle != nullptr) { esp_lcd_touch_del(_tp_handle); } if (_tp_io_handle != nullptr) { esp_lcd_panel_io_del(_tp_io_handle); } } void DisplayHandler::init_devices(bool set_display_ready /*= true*/) { ESP_LOGI("DisplayHandler", "Initializing display and touch..."); _epd_init(); _touch_init(); ESP_LOGI("DisplayHandler", "Display and touch initialized."); if (set_display_ready) { ESP_LOGI("DisplayHandler", "Setting display ready bit."); xEventGroupSetBits(_system_event_group, DISPLAY_READY_BIT | TOUCH_CALIBRATED_BIT); } } void DisplayHandler::epd_write_cmd(uint8_t cmd) { ESP_LOGI("DisplayHandler", "epd_write_cmd: waiting to send 0x%02X", cmd); xSemaphoreTake(_spi_mutex, portMAX_DELAY); _dangerous_epd_write_cmd_without_lock(cmd); xSemaphoreGive(_spi_mutex); ESP_LOGI("DisplayHandler", "epd_write_cmd: 0x%02X done", cmd); } void DisplayHandler::epd_write_data(uint8_t data) { ESP_LOGI("DisplayHandler", "epd_write_data: waiting to send 0x%02X", data); xSemaphoreTake(_spi_mutex, portMAX_DELAY); _dangerous_epd_write_data_without_lock(data); xSemaphoreGive(_spi_mutex); ESP_LOGI("DisplayHandler", "epd_write_data: 0x%02X done", data); } void DisplayHandler::epd_write_cmd_with_data(uint8_t cmd, const uint8_t* data, size_t data_len) { ESP_LOGI("DisplayHandler", "epd_write_cmd_with_data: waiting to send cmd 0x%02X with %u bytes of data", cmd, (unsigned)data_len); xSemaphoreTake(_spi_mutex, portMAX_DELAY); _dangerous_epd_write_cmd_without_lock(cmd); for (size_t i = 0; i < data_len; ++i) { _dangerous_epd_write_data_without_lock(data[i]); } xSemaphoreGive(_spi_mutex); ESP_LOGI("DisplayHandler", "epd_write_cmd_with_data: cmd 0x%02X with %u bytes of data done", cmd, (unsigned)data_len); } // // Private methods // void DisplayHandler::_dangerous_epd_write_cmd_without_lock(uint8_t cmd) { ESP_LOGI("DisplayHandler", "_dangerous_epd_write_cmd_without_lock: sending 0x%02X", cmd); gpio_set_level(PIN_DC, 0); // Command mode spi_transaction_t t {}; t.length = 8;t.tx_buffer = &cmd; esp_err_t err = spi_device_polling_transmit(_spi, &t); if (err != ESP_OK) { ESP_LOGE("DisplayHandler", "Failed to send data 0x%02X", cmd); } else { ESP_LOGI("DisplayHandler", "_dangerous_epd_write_cmd_without_lock: 0x%02X sent", cmd); } } void DisplayHandler::_dangerous_epd_write_data_without_lock(uint8_t data) { ESP_LOGI("DisplayHandler", "_dangerous_epd_write_data_without_lock: sending 0x%02X", data); gpio_set_level(PIN_DC, 1); // Data mode spi_transaction_t t = { }; t.length = 8; t.tx_buffer = &data; esp_err_t err = spi_device_polling_transmit(_spi, &t); if (err != ESP_OK) { ESP_LOGE("DisplayHandler", "Failed to send data 0x%02X", data); } else { ESP_LOGI("DisplayHandler", "_dangerous_epd_write_data_without_lock: 0x%02X sent", data); } } // required to be called by inheriting class after SPI device is created void DisplayHandler::_epd_init(void) { ESP_LOGI("DisplayHandler", "Initializing EPD..."); // 1. Hardware Reset gpio_set_level(PIN_RST, 0); vTaskDelay(pdMS_TO_TICKS(10)); gpio_set_level(PIN_RST, 1); vTaskDelay(pdMS_TO_TICKS(10)); // 2. Initialization Sequence const uint8_t panel_setting_data[] = { 0x1F }; epd_write_cmd_with_data(0x00, panel_setting_data, 1); // Panel Setting vTaskDelay(pdMS_TO_TICKS(10)); const uint8_t vcom_data[] = { 0x10, 0x07 }; epd_write_cmd_with_data(0x50, vcom_data, 2); // VCOM vTaskDelay(pdMS_TO_TICKS(10)); epd_write_cmd(0x04); // Power ON vTaskDelay(pdMS_TO_TICKS(100)); // Wait for power on // Check BUSY pin ESP_LOGI("DisplayHandler", "Waiting for EPD to be ready..."); while (gpio_get_level(PIN_BUSY) == 0) { // 0=BUSY, 1=FREE vTaskDelay(pdMS_TO_TICKS(10)); } ESP_LOGI("DisplayHandler", "EPD is ready."); const uint8_t booster_data[] = { 0x27, 0x27, 0x18, 0x17 }; epd_write_cmd_with_data(0x06, booster_data, 4); // Booster Soft Start vTaskDelay(pdMS_TO_TICKS(10)); // Enhanced display drive commands const uint8_t e0_data[] = { 0x02 }; epd_write_cmd_with_data(0xE0, e0_data, 1); const uint8_t e5_data[] = { 0x5A }; epd_write_cmd_with_data(0xE5, e5_data, 1); } void DisplayHandler::_touch_init(void) { ESP_LOGI("DisplayHandler", "Initializing touch..."); // 1. Initialize I2C Bus i2c_config_t conf = {}; conf.mode = I2C_MODE_MASTER; conf.sda_io_num = PIN_TOUCH_SDA; conf.scl_io_num = PIN_TOUCH_SCL; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; conf.scl_pullup_en = GPIO_PULLUP_ENABLE; conf.master.clk_speed = 400000; i2c_param_config(I2C_NUM_0, &conf); i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0); ESP_LOGI("DisplayHandler", "I2C driver installed"); // 2. Initialize GT911 ESP_LOGI("DisplayHandler", "Initializing GT911 touch controller..."); esp_lcd_panel_io_i2c_config_t tp_io_config = {}; // temporarily disable -Wmissing-field-initializers, as ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG macro does not set all fields #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" esp_lcd_panel_io_i2c_config_t default_tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); #pragma GCC diagnostic pop tp_io_config.dev_addr = default_tp_io_config.dev_addr; tp_io_config.control_phase_bytes = default_tp_io_config.control_phase_bytes; tp_io_config.dc_bit_offset = default_tp_io_config.dc_bit_offset; tp_io_config.lcd_cmd_bits = default_tp_io_config.lcd_cmd_bits; tp_io_config.flags = default_tp_io_config.flags; esp_lcd_new_panel_io_i2c(I2C_NUM_0, &tp_io_config, &_tp_io_handle); esp_lcd_touch_config_t tp_cfg = {}; tp_cfg.x_max = 800; tp_cfg.y_max = 480; tp_cfg.rst_gpio_num = PIN_RST; tp_cfg.int_gpio_num = PIN_TOUCH_IRQ; esp_lcd_touch_new_i2c_gt911(_tp_io_handle, &tp_cfg, &_tp_handle); ESP_LOGI("DisplayHandler", "GT911 touch controller initialized"); }