287 lines
12 KiB
C
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);
|
|
}
|