#include "esp_lcd_st7305.h" #include "soc/soc_caps.h" #include "esp_check.h" #include "esp_lcd_types.h" #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_lcd_panel_interface.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_commands.h" #include "driver/gpio.h" #include #include "esp_log.h" static const char *TAG = "st7305"; static esp_err_t panel_st7305_del(esp_lcd_panel_t *panel); static esp_err_t panel_st7305_reset(esp_lcd_panel_t *panel); static esp_err_t panel_st7305_init(esp_lcd_panel_t *panel); static esp_err_t panel_st7305_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data); static esp_err_t panel_st7305_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); static esp_err_t panel_st7305_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); static esp_err_t panel_st7305_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); static esp_err_t panel_st7305_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); static esp_err_t panel_st7305_disp_on_off(esp_lcd_panel_t *panel, bool off); typedef struct { esp_lcd_panel_t base; esp_lcd_panel_io_handle_t io; int reset_gpio_num; bool reset_level; int x_gap; int y_gap; int width; int height; uint8_t rotation; uint8_t madctl_val; const st7305_lcd_init_cmd_t *init_cmds; uint16_t init_cmds_size; uint8_t *draw_buffer; } st7305_panel_t; static const st7305_lcd_init_cmd_t st7305_init_cmds[] = { {0xD6, (uint8_t[]){0x17, 0x02}, 2, 0}, // NVM Load Control {0xD1, (uint8_t[]){0x01}, 1, 0}, // Booster Enable {0xC0, (uint8_t[]){0x11, 0x04}, 2, 0}, // Gate Voltage Setting {0xC1, (uint8_t[]){0x41, 0x41, 0x41, 0x41}, 4, 0}, // VSHP Setting {0xC2, (uint8_t[]){0x19, 0x19, 0x19, 0x19}, 4, 0}, // VSLP Setting {0xC4, (uint8_t[]){0x41, 0x41, 0x41, 0x41}, 4, 0}, // VSHN Setting {0xC5, (uint8_t[]){0x19, 0x13, 0x19, 0x19}, 4, 0}, // VSLN Setting {0xD8, (uint8_t[]){0xA6, 0xE9}, 2, 0}, // OSC Setting {0xB2, (uint8_t[]){0x05}, 1, 0}, // Frame Rate Control {0xB3, (uint8_t[]){0xE5, 0xF6, 0x05, 0x46, 0x77, 0x77, 0x77, 0x77, 0x76, 0x45}, 10, 0}, // Gate EQ HPM {0xB4, (uint8_t[]){0x05, 0x46, 0x77, 0x77, 0x77, 0x77, 0x76, 0x45}, 8, 0}, // Gate EQ LPM {0x62, (uint8_t[]){0x32, 0x03, 0x1F}, 3, 0}, // Gate Timing Control {0xB7, (uint8_t[]){0x13}, 1, 0}, // Source EQ Enable {0xB0, (uint8_t[]){0x64}, 1, 0}, // Gate Line Setting: 300 line (0x64 = 100 * 3) {0x11, NULL, 0, 200}, // Sleep out {0xC9, (uint8_t[]){0x00}, 1, 0}, // Source Voltage Select {0x36, (uint8_t[]){0x48}, 1, 0}, // Memory Data Access Control (MX=1, DO=1) {0x3A, (uint8_t[]){0x11}, 1, 0}, // Data Format Select: 1bpp {0xB9, (uint8_t[]){0x20}, 1, 0}, // Gamma Mode Setting {0xB8, (uint8_t[]){0x29}, 1, 0}, // Panel Setting {0x21, NULL, 0, 0}, // Display Inversion On {0x2A, (uint8_t[]){0x12, 0x2A}, 2, 0}, // Column Address Setting {0x2B, (uint8_t[]){0x00, 0xC7}, 2, 0}, // Row Address Setting {0x35, (uint8_t[]){0x00}, 1, 0}, // TE Line {0xD0, (uint8_t[]){0xFF}, 1, 0}, // Auto power down ON {0x38, NULL, 0, 0}, // High Power Mode ON {0x29, NULL, 0, 100}, // Display ON }; esp_err_t esp_lcd_new_panel_st7305(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) { esp_err_t ret = ESP_OK; st7305_panel_t *st7305 = NULL; gpio_config_t io_conf = { 0 }; ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); st7305 = (st7305_panel_t *)calloc(1, sizeof(st7305_panel_t)); ESP_GOTO_ON_FALSE(st7305, ESP_ERR_NO_MEM, err, TAG, "no mem for st7305 panel"); if (panel_dev_config->reset_gpio_num >= 0) { io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num; ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); } st7305->width = ST7305_WIDTH; st7305->height = ST7305_HEIGHT; st7305->madctl_val = 0x48; // MX=1, DO=1 st7305->rotation = 0; st7305->io = io; st7305->reset_gpio_num = panel_dev_config->reset_gpio_num; st7305->reset_level = panel_dev_config->flags.reset_active_high; if (panel_dev_config->vendor_config) { st7305->init_cmds = ((st7305_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds; st7305->init_cmds_size = ((st7305_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds_size; } else { st7305->init_cmds = st7305_init_cmds; st7305->init_cmds_size = sizeof(st7305_init_cmds) / sizeof(st7305_lcd_init_cmd_t); } st7305->draw_buffer = heap_caps_malloc(15000, MALLOC_CAP_DMA); ESP_GOTO_ON_FALSE(st7305->draw_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for st7305 draw buffer"); memset(st7305->draw_buffer, 0, 15000); st7305->base.del = panel_st7305_del; st7305->base.reset = panel_st7305_reset; st7305->base.init = panel_st7305_init; st7305->base.draw_bitmap = panel_st7305_draw_bitmap; st7305->base.invert_color = panel_st7305_invert_color; st7305->base.set_gap = panel_st7305_set_gap; st7305->base.mirror = panel_st7305_mirror; st7305->base.swap_xy = panel_st7305_swap_xy; #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) st7305->base.disp_off = panel_st7305_disp_on_off; #else st7305->base.disp_on_off = panel_st7305_disp_on_off; #endif *ret_panel = &(st7305->base); ESP_LOGD(TAG, "new st7305 panel @%p", st7305); return ESP_OK; err: if (st7305) { if (panel_dev_config->reset_gpio_num >= 0) { gpio_reset_pin(panel_dev_config->reset_gpio_num); } if (st7305->draw_buffer) { free(st7305->draw_buffer); } free(st7305); } return ret; } static esp_err_t panel_st7305_del(esp_lcd_panel_t *panel) { st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base); if (st7305->reset_gpio_num >= 0) { gpio_reset_pin(st7305->reset_gpio_num); } if (st7305->draw_buffer) { free(st7305->draw_buffer); } free(st7305); return ESP_OK; } static esp_err_t panel_st7305_reset(esp_lcd_panel_t *panel) { st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base); if (st7305->reset_gpio_num >= 0) { gpio_set_level(st7305->reset_gpio_num, !st7305->reset_level); vTaskDelay(pdMS_TO_TICKS(50)); gpio_set_level(st7305->reset_gpio_num, st7305->reset_level); vTaskDelay(pdMS_TO_TICKS(20)); gpio_set_level(st7305->reset_gpio_num, !st7305->reset_level); vTaskDelay(pdMS_TO_TICKS(50)); } return ESP_OK; } static esp_err_t panel_st7305_init(esp_lcd_panel_t *panel) { st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base); esp_lcd_panel_io_handle_t io = st7305->io; for (size_t i = 0; i < st7305->init_cmds_size; i++) { if (st7305->init_cmds[i].data_bytes > 0) { esp_lcd_panel_io_tx_param(io, st7305->init_cmds[i].cmd, st7305->init_cmds[i].data, st7305->init_cmds[i].data_bytes); } else { esp_lcd_panel_io_tx_param(io, st7305->init_cmds[i].cmd, NULL, 0); } if (st7305->init_cmds[i].delay_ms > 0) { vTaskDelay(pdMS_TO_TICKS(st7305->init_cmds[i].delay_ms)); } } // Explicitly clear display RAM to white if (st7305->draw_buffer) { memset(st7305->draw_buffer, 0xFF, 15000); // 0xFF is White uint8_t caset[] = {0x12, 0x2A}; uint8_t raset[] = {0x00, 0xC7}; esp_lcd_panel_io_tx_param(io, ST7305_CMD_CASET, caset, sizeof(caset)); esp_lcd_panel_io_tx_param(io, ST7305_CMD_RASET, raset, sizeof(raset)); esp_lcd_panel_io_tx_color(io, ST7305_CMD_RAMWR, st7305->draw_buffer, 15000); vTaskDelay(pdMS_TO_TICKS(50)); } return ESP_OK; } static esp_err_t panel_st7305_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) { st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base); esp_lcd_panel_io_handle_t io = st7305->io; const uint8_t *src = (const uint8_t *)color_data; // Convert from vertical-page format (SSD1306-style) to ST7305 landscape 2x4 block format for (int y = 0; y < 300; y++) { int inv_y = 299 - y; int block_y = inv_y >> 2; int local_y = inv_y & 3; int src_y_div_8 = y >> 3; int src_y_mod_8 = y & 7; uint8_t src_bit_mask = 1 << src_y_mod_8; for (int x = 0; x < 400; x++) { int byte_x = x >> 1; int local_x = x & 1; int dest_byte_idx = byte_x * 75 + block_y; int dest_bit_pos = 7 - ((local_y << 1) | local_x); int src_byte_idx = 400 * src_y_div_8 + x; // Read standard vertical page byte bit bool is_pixel_set = (src[src_byte_idx] & src_bit_mask) != 0; // Invert the pixel logic if required, standard: // esp_lvgl_port monochrome transform clears the bit (0) for light/chroma colors // and sets the bit (1) for dark/black. // In ST7305 display RAM, White/Light is 1, Black/Dark is 0. // So we write: 1 (White) if is_pixel_set is false (light), and 0 (Black) if is_pixel_set is true (dark). if (!is_pixel_set) { st7305->draw_buffer[dest_byte_idx] |= (1 << dest_bit_pos); } else { st7305->draw_buffer[dest_byte_idx] &= ~(1 << dest_bit_pos); } } } uint8_t caset[] = {0x12, 0x2A}; uint8_t raset[] = {0x00, 0xC7}; esp_lcd_panel_io_tx_param(io, ST7305_CMD_CASET, caset, sizeof(caset)); esp_lcd_panel_io_tx_param(io, ST7305_CMD_RASET, raset, sizeof(raset)); esp_lcd_panel_io_tx_color(io, ST7305_CMD_RAMWR, st7305->draw_buffer, 15000); return ESP_OK; } static esp_err_t panel_st7305_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) { st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base); return esp_lcd_panel_io_tx_param(st7305->io, invert_color_data ? ST7305_CMD_INVON : ST7305_CMD_INVOFF, NULL, 0); } static esp_err_t panel_st7305_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) { st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base); esp_lcd_panel_io_handle_t io = st7305->io; if (mirror_x) { st7305->madctl_val |= ST7305_MADCTL_MX; } else { st7305->madctl_val &= ~ST7305_MADCTL_MX; } if (mirror_y) { st7305->madctl_val |= ST7305_MADCTL_MY; } else { st7305->madctl_val &= ~ST7305_MADCTL_MY; } return esp_lcd_panel_io_tx_param(io, ST7305_CMD_MADCTL, &st7305->madctl_val, 1); } static esp_err_t panel_st7305_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) { st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base); esp_lcd_panel_io_handle_t io = st7305->io; if (swap_axes) { st7305->madctl_val |= ST7305_MADCTL_MV; } else { st7305->madctl_val &= ~ST7305_MADCTL_MV; } return esp_lcd_panel_io_tx_param(io, ST7305_CMD_MADCTL, (uint8_t[]) { st7305->madctl_val }, 1); } static esp_err_t panel_st7305_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) { st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base); st7305->x_gap = x_gap; st7305->y_gap = y_gap; return ESP_OK; } static esp_err_t panel_st7305_disp_on_off(esp_lcd_panel_t *panel, bool off) { st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base); return esp_lcd_panel_io_tx_param(st7305->io, off ? ST7305_CMD_DISPOFF : ST7305_CMD_DISPON, NULL, 0); }