Files
tactility/Drivers/ST7305/Source/esp_lcd_st7305.c
T
2026-07-09 21:44:49 -04:00

287 lines
12 KiB
C

#include "esp_lcd_st7305.h"
#include "soc/soc_caps.h"
#include "esp_check.h"
#include "esp_lcd_types.h"
#include <stdlib.h>
#include <sys/cdefs.h>
#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 <string.h>
#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;
} 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[]){0x69, 0x69, 0x69, 0x69}, 4, 0}, // VSHP Setting
{0xC2, (uint8_t[]){0x19, 0x19, 0x19, 0x19}, 4, 0}, // VSLP Setting
{0xC4, (uint8_t[]){0x4B, 0x4B, 0x4B, 0x4B}, 4, 0}, // VSHN Setting
{0xC5, (uint8_t[]){0x19, 0x19, 0x19, 0x19}, 4, 0}, // VSLN Setting
{0xD8, (uint8_t[]){0x80, 0xE9}, 2, 0}, // OSC Setting
{0xB2, (uint8_t[]){0x02}, 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->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);
}
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);
}
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));
}
}
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;
// Output buffer size is 15000 bytes (400 * 300 / 8)
size_t lcd_buf_size = 15000;
uint8_t *temp_buffer = heap_caps_malloc(lcd_buf_size, MALLOC_CAP_DMA);
if (!temp_buffer) {
return ESP_ERR_NO_MEM;
}
memset(temp_buffer, 0, lcd_buf_size);
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) {
temp_buffer[dest_byte_idx] |= (1 << dest_bit_pos);
} else {
temp_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, temp_buffer, lcd_buf_size);
free(temp_buffer);
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_MY;
} else {
st7305->madctl_val &= ~ST7305_MADCTL_MY;
}
if (mirror_y) {
st7305->madctl_val |= ST7305_MADCTL_MX;
} else {
st7305->madctl_val &= ~ST7305_MADCTL_MX;
}
return esp_lcd_panel_io_tx_param(io, ST7305_CMD_MADCTL, (uint8_t[]) { 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);
}