Skip to content

Instantly share code, notes, and snippets.

@CypherpunkSamurai
Forked from fallenartist/Display_ST7701.cpp
Created September 2, 2025 15:06
Show Gist options
  • Select an option

  • Save CypherpunkSamurai/e2a7eb541ad1b9bc908ee512722d1f08 to your computer and use it in GitHub Desktop.

Select an option

Save CypherpunkSamurai/e2a7eb541ad1b9bc908ee512722d1f08 to your computer and use it in GitHub Desktop.

Revisions

  1. @fallenartist fallenartist created this gist Jan 28, 2025.
    426 changes: 426 additions & 0 deletions Display_ST7701.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,426 @@
    #include "Display_ST7701.h"

    spi_device_handle_t SPI_handle = NULL;
    esp_lcd_panel_handle_t panel_handle = NULL;
    void ST7701_WriteCommand(uint8_t cmd)
    {
    spi_transaction_t spi_tran = {
    .cmd = 0,
    .addr = cmd,
    .length = 0,
    .rxlength = 0,
    };
    spi_device_transmit(SPI_handle, &spi_tran);
    }
    void ST7701_WriteData(uint8_t data)
    {
    spi_transaction_t spi_tran = {
    .cmd = 1,
    .addr = data,
    .length = 0,
    .rxlength = 0,
    };
    spi_device_transmit(SPI_handle, &spi_tran);
    }

    void ST7701_CS_EN(){
    Set_EXIO(EXIO_PIN3,Low);
    vTaskDelay(pdMS_TO_TICKS(10));
    }
    void ST7701_CS_Dis(){
    Set_EXIO(EXIO_PIN3,High);
    vTaskDelay(pdMS_TO_TICKS(10));
    }
    void ST7701_Reset(){
    Set_EXIO(EXIO_PIN1,Low);
    vTaskDelay(pdMS_TO_TICKS(10));
    Set_EXIO(EXIO_PIN1,High);
    vTaskDelay(pdMS_TO_TICKS(50));
    }
    void ST7701_Init()
    {
    spi_bus_config_t buscfg = {
    .mosi_io_num = LCD_MOSI_PIN,
    .miso_io_num = -1,
    .sclk_io_num = LCD_CLK_PIN,
    .quadwp_io_num = -1,
    .quadhd_io_num = -1,
    .max_transfer_sz = 64, // ESP32 S3 max size is 64Kbytes
    };
    spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
    spi_device_interface_config_t devcfg = {
    .command_bits = 1,
    .address_bits = 8,
    .mode = SPI_MODE0,
    .clock_speed_hz = 40000000,
    .spics_io_num = -1,
    .queue_size = 1, // Not using queues
    };
    spi_bus_add_device(SPI2_HOST, &devcfg, &SPI_handle);

    ST7701_CS_EN();
    ST7701_WriteCommand(0xFF);
    ST7701_WriteData(0x77);
    ST7701_WriteData(0x01);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x10);

    ST7701_WriteCommand(0xC0);
    ST7701_WriteData(0x3B);//Scan line
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0xC1);
    ST7701_WriteData(0x0B); //VBP
    ST7701_WriteData(0x02);

    ST7701_WriteCommand(0xC2);
    ST7701_WriteData(0x07);
    ST7701_WriteData(0x02);

    ST7701_WriteCommand(0xCC);
    ST7701_WriteData(0x10);

    ST7701_WriteCommand(0xCD);//RGB format
    ST7701_WriteData(0x08);

    ST7701_WriteCommand(0xB0); // IPS
    ST7701_WriteData(0x00); // 255
    ST7701_WriteData(0x11); // 251
    ST7701_WriteData(0x16); // 247 down
    ST7701_WriteData(0x0e); // 239
    ST7701_WriteData(0x11); // 231
    ST7701_WriteData(0x06); // 203
    ST7701_WriteData(0x05); // 175
    ST7701_WriteData(0x09); // 147
    ST7701_WriteData(0x08); // 108
    ST7701_WriteData(0x21); // 80
    ST7701_WriteData(0x06); // 52
    ST7701_WriteData(0x13); // 24
    ST7701_WriteData(0x10); // 16
    ST7701_WriteData(0x29); // 8 down
    ST7701_WriteData(0x31); // 4
    ST7701_WriteData(0x18); // 0

    ST7701_WriteCommand(0xB1);// IPS
    ST7701_WriteData(0x00);// 255
    ST7701_WriteData(0x11);// 251
    ST7701_WriteData(0x16);// 247 down
    ST7701_WriteData(0x0e);// 239
    ST7701_WriteData(0x11);// 231
    ST7701_WriteData(0x07);// 203
    ST7701_WriteData(0x05);// 175
    ST7701_WriteData(0x09);// 147
    ST7701_WriteData(0x09);// 108
    ST7701_WriteData(0x21);// 80
    ST7701_WriteData(0x05);// 52
    ST7701_WriteData(0x13);// 24
    ST7701_WriteData(0x11);// 16
    ST7701_WriteData(0x2a);// 8 down
    ST7701_WriteData(0x31);// 4
    ST7701_WriteData(0x18);// 0

    ST7701_WriteCommand(0xFF);
    ST7701_WriteData(0x77);
    ST7701_WriteData(0x01);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x11);

    ST7701_WriteCommand(0xB0); //VOP 3.5375+ *x 0.0125
    ST7701_WriteData(0x6d); //5D

    ST7701_WriteCommand(0xB1); //VCOM amplitude setting
    ST7701_WriteData(0x37); //

    ST7701_WriteCommand(0xB2); //VGH Voltage setting
    ST7701_WriteData(0x81); //12V

    ST7701_WriteCommand(0xB3);
    ST7701_WriteData(0x80);

    ST7701_WriteCommand(0xB5); //VGL Voltage setting
    ST7701_WriteData(0x43); //-8.3V

    ST7701_WriteCommand(0xB7);
    ST7701_WriteData(0x85);

    ST7701_WriteCommand(0xB8);
    ST7701_WriteData(0x20);

    ST7701_WriteCommand(0xC1);
    ST7701_WriteData(0x78);

    ST7701_WriteCommand(0xC2);
    ST7701_WriteData(0x78);

    ST7701_WriteCommand(0xD0);
    ST7701_WriteData(0x88);

    ST7701_WriteCommand(0xE0);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x02);

    ST7701_WriteCommand(0xE1);
    ST7701_WriteData(0x03);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x04);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x20);
    ST7701_WriteData(0x20);

    ST7701_WriteCommand(0xE2);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0xE3);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x11);
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0xE4);
    ST7701_WriteData(0x22);
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0xE5);
    ST7701_WriteData(0x05);
    ST7701_WriteData(0xEC);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0x07);
    ST7701_WriteData(0xEE);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0xE6);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x11);
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0xE7);
    ST7701_WriteData(0x22);
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0xE8);
    ST7701_WriteData(0x06);
    ST7701_WriteData(0xED);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0x08);
    ST7701_WriteData(0xEF);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0xEB);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x40);
    ST7701_WriteData(0x40);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0xED);
    ST7701_WriteData(0xFF);
    ST7701_WriteData(0xFF);
    ST7701_WriteData(0xFF);
    ST7701_WriteData(0xBA);
    ST7701_WriteData(0x0A);
    ST7701_WriteData(0xBF);
    ST7701_WriteData(0x45);
    ST7701_WriteData(0xFF);
    ST7701_WriteData(0xFF);
    ST7701_WriteData(0x54);
    ST7701_WriteData(0xFB);
    ST7701_WriteData(0xA0);
    ST7701_WriteData(0xAB);
    ST7701_WriteData(0xFF);
    ST7701_WriteData(0xFF);
    ST7701_WriteData(0xFF);

    ST7701_WriteCommand(0xEF);
    ST7701_WriteData(0x10);
    ST7701_WriteData(0x0D);
    ST7701_WriteData(0x04);
    ST7701_WriteData(0x08);
    ST7701_WriteData(0x3F);
    ST7701_WriteData(0x1F);

    ST7701_WriteCommand(0xFF);
    ST7701_WriteData(0x77);
    ST7701_WriteData(0x01);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x13);

    ST7701_WriteCommand(0xEF);
    ST7701_WriteData(0x08);

    ST7701_WriteCommand(0xFF);
    ST7701_WriteData(0x77);
    ST7701_WriteData(0x01);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);
    ST7701_WriteData(0x00);


    ST7701_WriteCommand(0x36);
    ST7701_WriteData(0x00);

    ST7701_WriteCommand(0x3A);
    ST7701_WriteData(0x66);

    ST7701_WriteCommand(0x11);

    vTaskDelay(pdMS_TO_TICKS(480));

    ST7701_WriteCommand(0x20); //
    vTaskDelay(pdMS_TO_TICKS(120));
    ST7701_WriteCommand(0x29);
    ST7701_CS_Dis();

    // RGB
    esp_lcd_rgb_panel_config_t rgb_config = {
    .clk_src = LCD_CLK_SRC_DEFAULT,
    .timings = {
    .pclk_hz = ESP_PANEL_LCD_RGB_TIMING_FREQ_HZ,
    .h_res = ESP_PANEL_LCD_HEIGHT,
    .v_res = ESP_PANEL_LCD_WIDTH,
    .hsync_pulse_width = ESP_PANEL_LCD_RGB_TIMING_HPW,
    .hsync_back_porch = ESP_PANEL_LCD_RGB_TIMING_HBP,
    .hsync_front_porch = ESP_PANEL_LCD_RGB_TIMING_HFP,
    .vsync_pulse_width = ESP_PANEL_LCD_RGB_TIMING_VPW,
    .vsync_back_porch = ESP_PANEL_LCD_RGB_TIMING_VBP,
    .vsync_front_porch = ESP_PANEL_LCD_RGB_TIMING_VFP,
    .flags = {
    .hsync_idle_low = 0, /*!< The hsync signal is low in IDLE state */
    .vsync_idle_low = 0, /*!< The vsync signal is low in IDLE state */
    .de_idle_high = 0, /*!< The de signal is high in IDLE state */
    .pclk_active_neg = false,
    .pclk_idle_high = 0, /*!< The PCLK stays at high level in IDLE phase */
    },
    },
    .data_width = ESP_PANEL_LCD_RGB_DATA_WIDTH,
    .bits_per_pixel = ESP_PANEL_LCD_RGB_PIXEL_BITS,
    .num_fbs = ESP_PANEL_LCD_RGB_FRAME_BUF_NUM,
    .bounce_buffer_size_px = 10 * ESP_PANEL_LCD_HEIGHT,
    .psram_trans_align = 64,
    .hsync_gpio_num = ESP_PANEL_LCD_PIN_NUM_RGB_HSYNC,
    .vsync_gpio_num = ESP_PANEL_LCD_PIN_NUM_RGB_VSYNC,
    .de_gpio_num = ESP_PANEL_LCD_PIN_NUM_RGB_DE,
    .pclk_gpio_num = ESP_PANEL_LCD_PIN_NUM_RGB_PCLK,
    .disp_gpio_num = ESP_PANEL_LCD_PIN_NUM_RGB_DISP,
    .data_gpio_nums = {
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA0,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA1,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA2,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA3,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA4,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA5,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA6,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA7,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA8,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA9,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA10,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA11,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA12,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA13,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA14,
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA15,
    },
    .flags = {
    .disp_active_low = 0,
    .refresh_on_demand = 0,
    .fb_in_psram = true,
    .double_fb = true,
    .no_fb = 0,
    .bb_invalidate_cache = 0,
    },
    };
    esp_lcd_new_rgb_panel(&rgb_config, &panel_handle);
    // esp_lcd_rgb_panel_event_callbacks_t cbs = {
    // .on_vsync = example_on_vsync_event,
    // };
    // esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, &disp_drv);
    esp_lcd_panel_reset(panel_handle);
    esp_lcd_panel_init(panel_handle);
    }

    bool example_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_data)
    {
    BaseType_t high_task_awoken = pdFALSE;
    return high_task_awoken == pdTRUE;
    }
    void LCD_Init() {
    ST7701_Reset();
    ST7701_Init();
    Touch_Init();
    Backlight_Init();
    }

    void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,uint8_t* color) {
    Xend = Xend + 1; // esp_lcd_panel_draw_bitmap: x_end End index on x-axis (x_end not included)
    Yend = Yend + 1; // esp_lcd_panel_draw_bitmap: y_end End index on y-axis (y_end not included)
    if (Xend >= ESP_PANEL_LCD_WIDTH)
    Xend = ESP_PANEL_LCD_WIDTH;
    if (Yend >= ESP_PANEL_LCD_HEIGHT)
    Yend = ESP_PANEL_LCD_HEIGHT;

    esp_lcd_panel_draw_bitmap(panel_handle, Xstart, Ystart, Xend, Yend, color); // x_end End index on x-axis (x_end not included)
    }


    // backlight
    uint8_t LCD_Backlight = 50;
    void Backlight_Init()
    {
    ledcAttach(LCD_Backlight_PIN, Frequency, Resolution);
    Set_Backlight(LCD_Backlight); //0~100
    }

    void Set_Backlight(uint8_t Light) //
    {
    if(Light > Backlight_MAX || Light < 0)
    printf("Set Backlight parameters in the range of 0 to 100 \r\n");
    else{
    uint32_t Backlight = Light*10;
    if(Backlight == 1000)
    Backlight = 1024;
    ledcWrite(LCD_Backlight_PIN, Backlight);
    }
    }
    73 changes: 73 additions & 0 deletions Display_ST7701.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,73 @@
    #pragma once
    #include <inttypes.h>
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "driver/spi_master.h"
    #include "driver/gpio.h"
    #include "esp_heap_caps.h"
    #include "esp_log.h"
    #include "esp_lcd_panel_ops.h"
    #include "esp_lcd_panel_io.h"
    #include "esp_lcd_panel_rgb.h"
    #include "driver/spi_master.h"
    #include "driver/gpio.h"
    #include "freertos/task.h"
    #include "TCA9554PWR.h"
    #include "Touch_CST820.h"

    #define LCD_CLK_PIN 2
    #define LCD_MOSI_PIN 1
    #define LCD_Backlight_PIN 6

    // Backlight
    #define PWM_Channel 1 // PWM Channel
    #define Frequency 20000 // PWM frequency
    #define Resolution 10 // PWM resolution ratio MAX:13
    #define Dutyfactor 500 // PWM Dutyfactor
    #define Backlight_MAX 100

    #define ESP_PANEL_LCD_WIDTH (480)
    #define ESP_PANEL_LCD_HEIGHT (480)
    #define ESP_PANEL_LCD_COLOR_BITS (16)
    #define ESP_PANEL_LCD_RGB_PIXEL_BITS (16)
    #define ESP_PANEL_LCD_RGB_DATA_WIDTH (16)
    #define ESP_PANEL_LCD_RGB_TIMING_FREQ_HZ (16 * 1000 * 1000)
    #define ESP_PANEL_LCD_RGB_TIMING_HPW (8)
    #define ESP_PANEL_LCD_RGB_TIMING_HBP (10)
    #define ESP_PANEL_LCD_RGB_TIMING_HFP (50)
    #define ESP_PANEL_LCD_RGB_TIMING_VPW (3)
    #define ESP_PANEL_LCD_RGB_TIMING_VBP (8)
    #define ESP_PANEL_LCD_RGB_TIMING_VFP (8)
    #define ESP_PANEL_LCD_RGB_FRAME_BUF_NUM (2)
    #define ESP_PANEL_LCD_RGB_BOUNCE_BUF_SIZE (ESP_PANEL_LCD_WIDTH * 10)

    // Pin definitions
    #define ESP_PANEL_LCD_PIN_NUM_RGB_HSYNC (38)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_VSYNC (39)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DE (40)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_PCLK (41)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA0 (5)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA1 (45)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA2 (48)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA3 (47)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA4 (21)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA5 (14)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA6 (13)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA7 (12)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA8 (11)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA9 (10)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA10 (9)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA11 (46)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA12 (3)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA13 (8)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA14 (18)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DATA15 (17)
    #define ESP_PANEL_LCD_PIN_NUM_RGB_DISP (-1)

    extern uint8_t LCD_Backlight;
    extern esp_lcd_panel_handle_t panel_handle;

    void LCD_Init();
    void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint8_t* color);
    void Backlight_Init();
    void Set_Backlight(uint8_t Light);
    35 changes: 35 additions & 0 deletions I2C_Driver.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    #include "I2C_Driver.h"

    void I2C_Init(void) {
    Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
    }

    // 寄存器地址为 8 位的
    bool I2C_Read(uint8_t Driver_addr, uint8_t Reg_addr, uint8_t *Reg_data, uint32_t Length)
    {
    Wire.beginTransmission(Driver_addr);
    Wire.write(Reg_addr);
    if ( Wire.endTransmission(true)){
    printf("The I2C transmission fails. - I2C Read\r\n");
    return -1;
    }
    Wire.requestFrom(Driver_addr, Length);
    for (int i = 0; i < Length; i++) {
    *Reg_data++ = Wire.read();
    }
    return 0;
    }
    bool I2C_Write(uint8_t Driver_addr, uint8_t Reg_addr, const uint8_t *Reg_data, uint32_t Length)
    {
    Wire.beginTransmission(Driver_addr);
    Wire.write(Reg_addr);
    for (int i = 0; i < Length; i++) {
    Wire.write(*Reg_data++);
    }
    if ( Wire.endTransmission(true))
    {
    printf("The I2C transmission fails. - I2C Write\r\n");
    return -1;
    }
    return 0;
    }
    11 changes: 11 additions & 0 deletions I2C_Driver.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    #pragma once
    #include <Wire.h>

    #define I2C_SCL_PIN 7
    #define I2C_SDA_PIN 15


    void I2C_Init(void);

    bool I2C_Read(uint8_t Driver_addr, uint8_t Reg_addr, uint8_t *Reg_data, uint32_t Length);
    bool I2C_Write(uint8_t Driver_addr, uint8_t Reg_addr, const uint8_t *Reg_data, uint32_t Length);
    263 changes: 263 additions & 0 deletions LCD_Hello.ino
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,263 @@
    #include <Arduino_GFX_Library.h>
    #include "TCA9554PWR.h"
    #include "Display_ST7701.h"
    #include "Touch_CST820.h"

    // Display dimensions
    #define DISPLAY_WIDTH 480
    #define DISPLAY_HEIGHT 480

    // Color definitions
    #define COLOR_BLACK 0x0000
    #define COLOR_BLUE 0x001F
    #define COLOR_RED 0xF800
    #define COLOR_WHITE 0xFFFF

    // Custom ST7701 Display Driver that uses existing initialization
    class Arduino_ST7701 : public Arduino_GFX {
    public:
    Arduino_ST7701(Arduino_ESP32RGBPanel *panel, int16_t w = DISPLAY_WIDTH, int16_t h = DISPLAY_HEIGHT)
    : Arduino_GFX(w, h), _panel(panel) {}

    bool begin(int32_t speed = GFX_NOT_DEFINED) override {
    _panel->begin();
    LCD_Init();
    return true;
    }

    protected:
    void writePixelPreclipped(int16_t x, int16_t y, uint16_t color) override {
    esp_lcd_panel_draw_bitmap(panel_handle, x, y, x + 1, y + 1, &color);
    }

    void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) override {
    if (h < 0) {
    y += h + 1;
    h = -h;
    }
    uint16_t *line = (uint16_t *)malloc(h * sizeof(uint16_t));
    if (line) {
    for (int16_t i = 0; i < h; i++) {
    line[i] = color;
    }
    esp_lcd_panel_draw_bitmap(panel_handle, x, y, x + 1, y + h, line);
    free(line);
    }
    }

    void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) override {
    if (w < 0) {
    x += w + 1;
    w = -w;
    }
    uint16_t *line = (uint16_t *)malloc(w * sizeof(uint16_t));
    if (line) {
    for (int16_t i = 0; i < w; i++) {
    line[i] = color;
    }
    esp_lcd_panel_draw_bitmap(panel_handle, x, y, x + w, y + 1, line);
    free(line);
    }
    }

    void writeFillRectPreclipped(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) override {
    if (w < 0) {
    x += w + 1;
    w = -w;
    }
    if (h < 0) {
    y += h + 1;
    h = -h;
    }
    uint16_t *buffer = (uint16_t *)malloc(w * h * sizeof(uint16_t));
    if (buffer) {
    for (int32_t i = 0; i < w * h; i++) {
    buffer[i] = color;
    }
    esp_lcd_panel_draw_bitmap(panel_handle, x, y, x + w, y + h, buffer);
    free(buffer);
    }
    }

    private:
    Arduino_ESP32RGBPanel* _panel;
    };

    // Error handling
    enum InitStatus {
    INIT_OK = 0,
    INIT_I2C_FAILED,
    INIT_IO_EXPANDER_FAILED,
    INIT_DISPLAY_FAILED,
    INIT_TOUCH_FAILED
    };

    // Create RGB Panel instance
    Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel(
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA0, // B0
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA1, // B1
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA2, // B2
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA3, // B3
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA4, // B4
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA5, // G0
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA6, // G1
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA7, // G2
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA8, // G3
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA9, // G4
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA10, // G5
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA11, // R0
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA12, // R1
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA13, // R2
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA14, // R3
    ESP_PANEL_LCD_PIN_NUM_RGB_DATA15, // R4
    ESP_PANEL_LCD_PIN_NUM_RGB_PCLK, // PCLK
    ESP_PANEL_LCD_PIN_NUM_RGB_VSYNC, // VSYNC
    ESP_PANEL_LCD_PIN_NUM_RGB_HSYNC, // HSYNC
    ESP_PANEL_LCD_PIN_NUM_RGB_DE, // DE
    DISPLAY_WIDTH, // Width
    DISPLAY_HEIGHT, // Height
    16, // hsync_polarity
    10, // hsync_front_porch
    8, // hsync_pulse_width
    50, // hsync_back_porch
    3, // vsync_polarity
    8, // vsync_front_porch
    8, // vsync_pulse_width
    8 // vsync_back_porch
    );

    // Create display driver instance
    Arduino_ST7701 *gfx = new Arduino_ST7701(rgbpanel);

    // Touch variables
    volatile bool touch_enabled = false;
    uint32_t last_touch_time = 0;
    const uint32_t TOUCH_DEBOUNCE_MS = 50;

    // Function prototypes
    InitStatus initializeHardware();
    void handleTouch();
    void drawTouchPoint(uint16_t x, uint16_t y, bool pressed);
    void IRAM_ATTR touchISR();

    InitStatus initializeHardware() {
    // Initialize I2C
    Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
    delay(100);

    // Initialize IO Expander
    TCA9554PWR_Init(0x00);
    uint8_t io_status = Read_EXIOS(TCA9554_OUTPUT_REG);
    if (io_status == 0xFF) {
    Serial.println("IO Expander initialization failed!");
    return INIT_IO_EXPANDER_FAILED;
    }

    // Enable display power
    Set_EXIO(EXIO_PIN8, Low);
    delay(10);

    // Initialize display using Arduino_GFX begin()
    if (!static_cast<Arduino_GFX*>(gfx)->begin()) {
    Serial.println("Display initialization failed!");
    return INIT_DISPLAY_FAILED;
    }

    // Initialize touch controller
    if (!Touch_Init()) {
    Serial.println("Touch controller initialization failed!");
    return INIT_TOUCH_FAILED;
    }

    // Set up touch interrupt
    pinMode(CST820_INT_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(CST820_INT_PIN), touchISR, FALLING);
    touch_enabled = true;

    return INIT_OK;
    }

    void drawTouchPoint(uint16_t x, uint16_t y, bool pressed) {
    const uint16_t radius = 5;
    uint16_t color = pressed ? COLOR_RED : COLOR_BLUE;

    if (!pressed) {
    gfx->fillCircle(x, y, radius + 1, COLOR_BLUE);
    return;
    }

    gfx->fillCircle(x, y, radius, color);
    gfx->drawCircle(x, y, radius + 1, COLOR_WHITE);
    }

    void handleTouch() {
    if (!touch_enabled) return;

    uint32_t current_time = millis();
    if (current_time - last_touch_time < TOUCH_DEBOUNCE_MS) return;
    last_touch_time = current_time;

    Touch_Read_Data();

    if (touch_data.points > 0) {
    uint16_t x = constrain(touch_data.x, 0, DISPLAY_WIDTH - 1);
    uint16_t y = constrain(touch_data.y, 0, DISPLAY_HEIGHT - 1);

    drawTouchPoint(x, y, true);

    char buf[32];
    snprintf(buf, sizeof(buf), "Touch: %d,%d ", x, y);
    gfx->setTextColor(COLOR_WHITE, COLOR_BLUE);
    gfx->setCursor(10, 10);
    gfx->print(buf);

    if (touch_data.gesture != NONE) {
    gfx->setCursor(10, 30);
    gfx->print("Gesture: ");
    gfx->print(Touch_GestureName());
    gfx->print(" ");
    }
    }
    }

    void IRAM_ATTR touchISR() {
    Touch_CST820_ISR();
    }

    void setup() {
    Serial.begin(115200);
    Serial.println("ESP32-S3 Touch LCD Hello World");

    InitStatus status = initializeHardware();
    if (status != INIT_OK) {
    Serial.print("Initialization failed with status: ");
    Serial.println(status);
    while (1) {
    delay(1000);
    Serial.print(".");
    }
    }

    gfx->setRotation(0);
    gfx->fillScreen(COLOR_BLUE);

    gfx->setTextColor(COLOR_WHITE);
    gfx->setTextSize(3);

    const char* text = "Touch the screen!";
    int16_t x1, y1;
    uint16_t w, h;
    gfx->getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
    int x = (DISPLAY_WIDTH - w) / 2;
    int y = (DISPLAY_HEIGHT - h) / 2;

    gfx->setCursor(x, y);
    gfx->println(text);

    Serial.println("Setup complete!");
    }

    void loop() {
    handleTouch();
    delay(10);
    }
    96 changes: 96 additions & 0 deletions TCA9554PWR.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,96 @@
    #include "TCA9554PWR.h"

    /***************************************************** Operation register REG ****************************************************/
    uint8_t I2C_Read_EXIO(uint8_t REG) // Read the value of the TCA9554PWR register REG
    {
    Wire.beginTransmission(TCA9554_ADDRESS);
    Wire.write(REG);
    uint8_t result = Wire.endTransmission();
    if (result != 0) {
    printf("The I2C transmission fails. - I2C Read EXIO\r\n");
    }
    Wire.requestFrom(TCA9554_ADDRESS, 1);
    uint8_t bitsStatus;
    if (Wire.available()) {
    bitsStatus = Wire.read();
    }
    return bitsStatus;
    }
    uint8_t I2C_Write_EXIO(uint8_t REG,uint8_t Data) // Write Data to the REG register of the TCA9554PWR
    {
    Wire.beginTransmission(TCA9554_ADDRESS);
    Wire.write(REG);
    Wire.write(Data);
    uint8_t result = Wire.endTransmission();
    if (result != 0) {
    printf("The I2C transmission fails. - I2C Write EXIO\r\n");
    return -1;
    }
    return 0;
    }
    /********************************************************** Set EXIO mode **********************************************************/
    void Mode_EXIO(uint8_t Pin,uint8_t State) // Set the mode of the TCA9554PWR Pin. The default is Output mode (output mode or input mode). State: 0= Output mode 1= input mode
    {
    uint8_t bitsStatus = I2C_Read_EXIO(TCA9554_CONFIG_REG);
    uint8_t Data = (0x01 << (Pin-1)) | bitsStatus;
    uint8_t result = I2C_Write_EXIO(TCA9554_CONFIG_REG,Data);
    if (result != 0) {
    printf("I/O Configuration Failure !!!\r\n");
    }
    }
    void Mode_EXIOS(uint8_t PinState) // Set the mode of the 7 pins from the TCA9554PWR with PinState
    {
    uint8_t result = I2C_Write_EXIO(TCA9554_CONFIG_REG,PinState);
    if (result != 0) {
    printf("I/O Configuration Failure !!!\r\n");
    }
    }
    /********************************************************** Read EXIO status **********************************************************/
    uint8_t Read_EXIO(uint8_t Pin) // Read the level of the TCA9554PWR Pin
    {
    uint8_t inputBits = I2C_Read_EXIO(TCA9554_INPUT_REG);
    uint8_t bitStatus = (inputBits >> (Pin-1)) & 0x01;
    return bitStatus;
    }
    uint8_t Read_EXIOS(uint8_t REG = TCA9554_INPUT_REG) // Read the level of all pins of TCA9554PWR, the default read input level state, want to get the current IO output state, pass the parameter TCA9554_OUTPUT_REG, such as Read_EXIOS(TCA9554_OUTPUT_REG);
    {
    uint8_t inputBits = I2C_Read_EXIO(REG);
    return inputBits;
    }

    /********************************************************** Set the EXIO output status **********************************************************/
    void Set_EXIO(uint8_t Pin,uint8_t State) // Sets the level state of the Pin without affecting the other pins
    {
    uint8_t Data;
    if(State < 2 && Pin < 9 && Pin > 0){
    uint8_t bitsStatus = Read_EXIOS(TCA9554_OUTPUT_REG);
    if(State == 1)
    Data = (0x01 << (Pin-1)) | bitsStatus;
    else if(State == 0)
    Data = (~(0x01 << (Pin-1))) & bitsStatus;
    uint8_t result = I2C_Write_EXIO(TCA9554_OUTPUT_REG,Data);
    if (result != 0) {
    printf("Failed to set GPIO!!!\r\n");
    }
    }
    else
    printf("Parameter error, please enter the correct parameter!\r\n");
    }
    void Set_EXIOS(uint8_t PinState) // Set 7 pins to the PinState state such as :PinState=0x23, 0010 0011 state (the highest bit is not used)
    {
    uint8_t result = I2C_Write_EXIO(TCA9554_OUTPUT_REG,PinState);
    if (result != 0) {
    printf("Failed to set GPIO!!!\r\n");
    }
    }
    /********************************************************** Flip EXIO state **********************************************************/
    void Set_Toggle(uint8_t Pin) // Flip the level of the TCA9554PWR Pin
    {
    uint8_t bitsStatus = Read_EXIO(Pin);
    Set_EXIO(Pin,(bool)!bitsStatus);
    }
    /********************************************************* TCA9554PWR Initializes the device ***********************************************************/
    void TCA9554PWR_Init(uint8_t PinState) // Set the seven pins to PinState state, for example :PinState=0x23, 0010 0011 State (Output mode or input mode) 0= Output mode 1= Input mode. The default value is output mode
    {
    Mode_EXIOS(PinState);
    }
    50 changes: 50 additions & 0 deletions TCA9554PWR.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,50 @@
    #pragma once

    #include <stdio.h>
    #include "I2C_Driver.h"

    #define TCA9554_EXIO1 0x01
    #define TCA9554_EXIO2 0x02
    #define TCA9554_EXIO3 0x03
    #define TCA9554_EXIO4 0x04
    #define TCA9554_EXIO5 0x05
    #define TCA9554_EXIO6 0x06
    #define TCA9554_EXIO7 0x07

    /****************************************************** The macro defines the TCA9554PWR information ******************************************************/

    #define TCA9554_ADDRESS 0x20 // TCA9554PWR I2C address
    // TCA9554PWR寄存器地址
    #define TCA9554_INPUT_REG 0x00 // Input register,input level
    #define TCA9554_OUTPUT_REG 0x01 // Output register, high and low level output
    #define TCA9554_Polarity_REG 0x02 // The Polarity Inversion register (register 2) allows polarity inversion of pins defined as inputs by the Configuration register.
    #define TCA9554_CONFIG_REG 0x03 // Configuration register, mode configuration


    #define Low 0
    #define High 1
    #define EXIO_PIN1 1
    #define EXIO_PIN2 2
    #define EXIO_PIN3 3
    #define EXIO_PIN4 4
    #define EXIO_PIN5 5
    #define EXIO_PIN6 6
    #define EXIO_PIN7 7
    #define EXIO_PIN8 8

    /***************************************************** Operation register REG ****************************************************/
    uint8_t I2C_Read_EXIO(uint8_t REG); // Read the value of the TCA9554PWR register REG
    uint8_t I2C_Write_EXIO(uint8_t REG,uint8_t Data); // Write Data to the REG register of the TCA9554PWR
    /********************************************************** Set EXIO mode **********************************************************/
    void Mode_EXIO(uint8_t Pin,uint8_t State); // Set the mode of the TCA9554PWR Pin. The default is Output mode (output mode or input mode). State: 0= Output mode 1= input mode
    void Mode_EXIOS(uint8_t PinState); // Set the mode of the 7 pins from the TCA9554PWR with PinState
    /********************************************************** Read EXIO status **********************************************************/
    uint8_t Read_EXIO(uint8_t Pin); // Read the level of the TCA9554PWR Pin
    uint8_t Read_EXIOS(uint8_t REG); // Read the level of all pins of TCA9554PWR, the default read input level state, want to get the current IO output state, pass the parameter TCA9554_OUTPUT_REG, such as Read_EXIOS(TCA9554_OUTPUT_REG);
    /********************************************************** Set the EXIO output status **********************************************************/
    void Set_EXIO(uint8_t Pin,uint8_t State); // Sets the level state of the Pin without affecting the other pins
    void Set_EXIOS(uint8_t PinState); // Set 7 pins to the PinState state such as :PinState=0x23, 0010 0011 state (the highest bit is not used)
    /********************************************************** Flip EXIO state **********************************************************/
    void Set_Toggle(uint8_t Pin); // Flip the level of the TCA9554PWR Pin
    /********************************************************* TCA9554PWR Initializes the device ***********************************************************/
    void TCA9554PWR_Init(uint8_t PinState = 0x00); // Set the seven pins to PinState state, for example :PinState=0x23, 0010 0011 State (the highest bit is not used) (Output mode or input mode) 0= Output mode 1= Input mode. The default value is output mode
    155 changes: 155 additions & 0 deletions Touch_CST820.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,155 @@
    #include "Touch_CST820.h"

    bool I2C_Read_Touch(uint8_t Driver_addr, uint8_t Reg_addr, uint8_t *Reg_data, uint32_t Length)
    {
    Wire.beginTransmission(Driver_addr);
    Wire.write(Reg_addr);
    if ( Wire.endTransmission(true)){
    printf("The I2C transmission fails. - I2C Read\r\n");
    return -1;
    }
    Wire.requestFrom(Driver_addr, Length);
    for (int i = 0; i < Length; i++) {
    *Reg_data++ = Wire.read();
    }
    return 0;
    }
    bool I2C_Write_Touch(uint8_t Driver_addr, uint8_t Reg_addr, const uint8_t *Reg_data, uint32_t Length)
    {
    Wire.beginTransmission(Driver_addr);
    Wire.write(Reg_addr);
    for (int i = 0; i < Length; i++) {
    Wire.write(*Reg_data++);
    }
    if ( Wire.endTransmission(true))
    {
    printf("The I2C transmission fails. - I2C Write\r\n");
    return -1;
    }
    return 0;
    }
    struct CST820_Touch touch_data = {0};
    uint8_t Touch_Init(void) {
    pinMode(CST820_INT_PIN, INPUT_PULLUP);
    CST820_Touch_Reset();
    CST820_AutoSleep(false);
    uint16_t Verification = CST820_Read_cfg();

    // attachInterrupt(CST820_INT_PIN, Touch_CST820_ISR, interrupt);

    return true;
    }
    /* Reset controller */
    uint8_t CST820_Touch_Reset(void)
    {
    Set_EXIO(EXIO_PIN2,Low);
    vTaskDelay(pdMS_TO_TICKS(10));
    Set_EXIO(EXIO_PIN2,High);
    vTaskDelay(pdMS_TO_TICKS(50));
    return true;
    }
    uint16_t CST820_Read_cfg(void) {

    uint8_t buf[3]={0};
    I2C_Read_Touch(CST820_ADDR, CST820_REG_Version,buf, 1);
    printf("TouchPad_Version:0x%02x\r\n", buf[0]);
    I2C_Read_Touch(CST820_ADDR, CST820_REG_ChipID, buf, 3);
    printf("ChipID:0x%02x ProjID:0x%02x FwVersion:0x%02x \r\n",buf[0], buf[1], buf[2]);

    return true;
    }
    /*!
    @brief Fall asleep automatically
    */
    void CST820_AutoSleep(bool Sleep_State) {
    CST820_Touch_Reset();
    uint8_t Sleep_State_Set = 0;
    if(Sleep_State)
    Sleep_State_Set = 0;
    else
    Sleep_State_Set = 0xFF;
    I2C_Write_Touch(CST820_ADDR, CST820_REG_DisAutoSleep, &Sleep_State_Set, 1);
    }

    // reads sensor and touches
    // updates Touch Points
    uint8_t Touch_Read_Data(void) {
    uint8_t buf[6];
    uint8_t touchpad_cnt = 0;
    I2C_Read_Touch(CST820_ADDR, CST820_REG_GestureID, buf, 6);
    /* touched gesture */
    if (buf[0] != 0x00)
    touch_data.gesture = (GESTURE)buf[0];
    if (buf[1] != 0x00) {

    noInterrupts();
    /* Number of touched points */
    touch_data.points = (uint8_t)buf[1];
    if(touch_data.points > CST820_LCD_TOUCH_MAX_POINTS)
    touch_data.points = CST820_LCD_TOUCH_MAX_POINTS;
    /* Fill coordinates */
    touch_data.x = ((buf[2] & 0x0F) << 8) + buf[3];
    touch_data.y = ((buf[4] & 0x0F) << 8) + buf[5];

    interrupts();
    // printf(" points=%d \r\n",touch_data.points);
    }
    return true;
    }
    void example_touchpad_read(void){
    Touch_Read_Data();
    if (touch_data.gesture != NONE || touch_data.points != 0x00) {
    // printf("Touch : X=%u Y=%u points=%d\r\n", touch_data.x , touch_data.y,touch_data.points);
    } else {
    // data->state = LV_INDEV_STATE_REL;
    }
    }
    void Touch_Loop(void){
    if(Touch_interrupts){
    Touch_interrupts = false;
    example_touchpad_read();
    }
    }

    /*!
    @brief handle interrupts
    */
    uint8_t Touch_interrupts;
    void IRAM_ATTR Touch_CST820_ISR(void) {
    Touch_interrupts = true;
    }

    /*!
    @brief get the gesture event name
    */
    String Touch_GestureName(void) {
    switch (touch_data.gesture) {
    case NONE:
    return "NONE";
    break;
    case SWIPE_DOWN:
    return "SWIPE DOWN";
    break;
    case SWIPE_UP:
    return "SWIPE UP";
    break;
    case SWIPE_LEFT:
    return "SWIPE LEFT";
    break;
    case SWIPE_RIGHT:
    return "SWIPE RIGHT";
    break;
    case SINGLE_CLICK:
    return "SINGLE CLICK";
    break;
    case DOUBLE_CLICK:
    return "DOUBLE CLICK";
    break;
    case LONG_PRESS:
    return "LONG PRESS";
    break;
    default:
    return "UNKNOWN";
    break;
    }
    }
    46 changes: 46 additions & 0 deletions Touch_CST820.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,46 @@
    // Touch_CST820.h
    #pragma once
    #include "Arduino.h"
    #include "I2C_Driver.h"
    #include "TCA9554PWR.h"

    #define CST820_ADDR 0x15
    #define CST820_INT_PIN 16

    #define CST820_LCD_TOUCH_MAX_POINTS (1)

    enum GESTURE {
    NONE = 0x00,
    SWIPE_UP = 0x01,
    SWIPE_DOWN = 0x02,
    SWIPE_LEFT = 0x03,
    SWIPE_RIGHT = 0x04,
    SINGLE_CLICK = 0x05,
    DOUBLE_CLICK = 0x0B,
    LONG_PRESS = 0x0C
    };

    #define CST820_REG_Mode 0x00
    #define CST820_REG_GestureID 0x01
    #define CST820_REG_Version 0x15
    #define CST820_REG_ChipID 0xA7
    #define CST820_REG_ProjID 0xA8
    #define CST820_REG_FwVersion 0xA9
    #define CST820_REG_AutoSleepTime 0xF9
    #define CST820_REG_DisAutoSleep 0xFE

    extern uint8_t Touch_interrupts;
    extern struct CST820_Touch {
    uint8_t points; // Number of touch points
    GESTURE gesture; // Gesture type
    uint16_t x; // X coordinate
    uint16_t y; // Y coordinate
    } touch_data;

    uint8_t Touch_Init();
    uint8_t CST820_Touch_Reset(void);
    void CST820_AutoSleep(bool Sleep_State);
    uint16_t CST820_Read_cfg(void);
    String Touch_GestureName(void);
    uint8_t Touch_Read_Data(void);
    void IRAM_ATTR Touch_CST820_ISR(void);