WT32 SC01 Plus (#416)

This commit is contained in:
Shadowtrance
2025-11-29 02:40:24 +10:00
committed by GitHub
parent 8de88dd3fd
commit 62124541e9
13 changed files with 596 additions and 2 deletions
@@ -0,0 +1,275 @@
#include "St7796i8080Display.h"
#include <Tactility/Log.h>
#include <driver/gpio.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <esp_lvgl_port.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <lvgl.h>
constexpr auto TAG = "St7796i8080Display";
static St7796i8080Display* g_display_instance = nullptr;
St7796i8080Display::St7796i8080Display(const Configuration& config)
: configuration(config), lock(std::make_shared<std::mutex>()) {
// Validate configuration
if (!configuration.isValid()) {
TT_LOG_E(TAG, "Invalid configuration: resolution must be set");
return;
}
}
bool St7796i8080Display::createI80Bus() {
TT_LOG_I(TAG, "Creating I80 bus");
// Create I80 bus configuration
esp_lcd_i80_bus_config_t bus_cfg = {
.dc_gpio_num = configuration.dcPin,
.wr_gpio_num = configuration.wrPin,
.clk_src = LCD_CLK_SRC_PLL160M,
.data_gpio_nums = {
configuration.dataPins[0], configuration.dataPins[1],
configuration.dataPins[2], configuration.dataPins[3],
configuration.dataPins[4], configuration.dataPins[5],
configuration.dataPins[6], configuration.dataPins[7],
},
.bus_width = configuration.busWidth,
.max_transfer_bytes = configuration.bufferSize * sizeof(uint16_t),
.psram_trans_align = 64,
.sram_trans_align = 4
};
if (esp_lcd_new_i80_bus(&bus_cfg, &i80BusHandle) != ESP_OK) {
TT_LOG_E(TAG, "Failed to create I80 bus");
return false;
}
return true;
}
bool St7796i8080Display::createPanelIO() {
TT_LOG_I(TAG, "Creating panel IO");
// Create panel IO
esp_lcd_panel_io_i80_config_t io_cfg = {
.cs_gpio_num = configuration.csPin,
.pclk_hz = configuration.pixelClockFrequency,
.trans_queue_depth = configuration.transactionQueueDepth,
.on_color_trans_done = nullptr,
.user_ctx = nullptr,
.lcd_cmd_bits = configuration.lcdCmdBits,
.lcd_param_bits = configuration.lcdParamBits,
.dc_levels = {
.dc_idle_level = configuration.dcLevels.dcIdleLevel,
.dc_cmd_level = configuration.dcLevels.dcCmdLevel,
.dc_dummy_level = configuration.dcLevels.dcDummyLevel,
.dc_data_level = configuration.dcLevels.dcDataLevel,
},
.flags = {
.cs_active_high = configuration.flags.csActiveHigh,
.reverse_color_bits = configuration.flags.reverseColorBits,
.swap_color_bytes = configuration.flags.swapColorBytes,
.pclk_active_neg = configuration.flags.pclkActiveNeg,
.pclk_idle_low = configuration.flags.pclkIdleLow
}
};
if (esp_lcd_new_panel_io_i80(i80BusHandle, &io_cfg, &ioHandle) != ESP_OK) {
TT_LOG_E(TAG, "Failed to create panel");
return false;
}
return true;
}
bool St7796i8080Display::createPanel() {
TT_LOG_I(TAG, "Configuring panel");
// Create ST7796 panel
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = configuration.resetPin,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
.data_endian = LCD_RGB_DATA_ENDIAN_LITTLE,
.bits_per_pixel = 16,
.flags = {
.reset_active_high = false
},
.vendor_config = nullptr
};
if (esp_lcd_new_panel_st7796(ioHandle, &panel_config, &panelHandle) != ESP_OK) {
TT_LOG_E(TAG, "Failed to create panel");
return false;
}
// Reset panel
if (esp_lcd_panel_reset(panelHandle) != ESP_OK) {
TT_LOG_E(TAG, "Failed to reset panel");
return false;
}
// Initialize panel
if (esp_lcd_panel_init(panelHandle) != ESP_OK) {
TT_LOG_E(TAG, "Failed to init panel");
return false;
}
// Set swap XY
if (esp_lcd_panel_swap_xy(panelHandle, configuration.swapXY) != ESP_OK) {
TT_LOG_E(TAG, "Failed to swap XY ");
return false;
}
// Set mirror
if (esp_lcd_panel_mirror(panelHandle, configuration.mirrorX, configuration.mirrorY) != ESP_OK) {
TT_LOG_E(TAG, "Failed to set panel to mirror");
return false;
}
// Set inversion
if (esp_lcd_panel_invert_color(panelHandle, configuration.invertColor) != ESP_OK) {
TT_LOG_E(TAG, "Failed to set panel to invert");
return false;
}
// Turn on display
if (esp_lcd_panel_disp_on_off(panelHandle, true) != ESP_OK) {
TT_LOG_E(TAG, "Failed to turn display on");
return false;
}
return true;
}
bool St7796i8080Display::start() {
TT_LOG_I(TAG, "Initializing I8080 ST7796 Display hardware...");
// Calculate buffer size if needed
configuration.calculateBufferSize();
// Allocate buffer
size_t buffer_size = configuration.bufferSize * LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565);
displayBuffer = (uint8_t*)heap_caps_malloc(buffer_size, MALLOC_CAP_DMA);
if (!displayBuffer) {
TT_LOG_E(TAG, "Failed to allocate display buffer");
return false;
}
// Create I80 bus
if (!createI80Bus()) {
stop();
return false;
}
// Create panel IO
if (!createPanelIO()) {
stop();
return false;
}
// Create panel
if (!createPanel()) {
stop();
return false;
}
TT_LOG_I(TAG, "Display hardware initialized");
return true;
}
bool St7796i8080Display::stop() {
// Turn off display
if (panelHandle) {
esp_lcd_panel_disp_on_off(panelHandle, false);
}
// Destroy in reverse order: panel, IO, bus
if (panelHandle) {
esp_lcd_panel_del(panelHandle);
panelHandle = nullptr;
}
if (ioHandle) {
esp_lcd_panel_io_del(ioHandle);
ioHandle = nullptr;
}
if (i80BusHandle) {
esp_lcd_del_i80_bus(i80BusHandle);
i80BusHandle = nullptr;
}
// Free buffer 1
if (displayBuffer) {
heap_caps_free(displayBuffer);
displayBuffer = nullptr;
}
// Turn off backlight
if (configuration.backlightDutyFunction) {
configuration.backlightDutyFunction(0);
}
return true;
}
bool St7796i8080Display::startLvgl() {
TT_LOG_I(TAG, "Initializing LVGL for ST7796 display");
// Don't reinitialize hardware if it's already done
if (!ioHandle) {
TT_LOG_I(TAG, "Hardware not initialized, calling start()");
if (!start()) {
TT_LOG_E(TAG, "Hardware initialization failed");
return false;
}
} else {
TT_LOG_I(TAG, "Hardware already initialized, skipping");
}
// Create LVGL display using lvgl_port
lvgl_port_display_cfg_t display_cfg = {
.io_handle = ioHandle,
.panel_handle = panelHandle,
.control_handle = nullptr,
.buffer_size = configuration.bufferSize,
.double_buffer = false,
.trans_size = 0,
.hres = configuration.horizontalResolution,
.vres = configuration.verticalResolution,
.monochrome = false,
.rotation = {
.swap_xy = configuration.swapXY,
.mirror_x = configuration.mirrorX,
.mirror_y = configuration.mirrorY,
},
.color_format = LV_COLOR_FORMAT_RGB565,
.flags = {
.buff_dma = true,
.buff_spiram = false,
.sw_rotate = false,
.swap_bytes = false,
.full_refresh = false,
.direct_mode = false
}
};
// Create the LVGL display
lvglDisplay = lvgl_port_add_disp(&display_cfg);
if (!lvglDisplay) {
TT_LOG_E(TAG, "Failed to create LVGL display");
return false;
}
g_display_instance = this;
TT_LOG_I(TAG, "LVGL display created successfully");
return true;
}
bool St7796i8080Display::stopLvgl() {
if (lvglDisplay) {
lvgl_port_remove_disp(lvglDisplay);
lvglDisplay = nullptr;
}
return true;
}
@@ -0,0 +1,136 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_types.h>
#include <esp_lcd_st7796.h>
#include <driver/gpio.h>
#include <array>
#include <memory>
#include <functional>
#include <mutex>
#include <string>
class St7796i8080Display : public tt::hal::display::DisplayDevice {
public:
struct Configuration {
// Pin configuration
gpio_num_t csPin;
gpio_num_t dcPin;
gpio_num_t wrPin;
std::array<gpio_num_t, 8> dataPins;
gpio_num_t resetPin;
gpio_num_t backlightPin;
// Display resolution configuration
uint16_t horizontalResolution;
uint16_t verticalResolution;
// Bus configuration
unsigned int pixelClockFrequency = 20 * 1000 * 1000; // 20MHz default
size_t busWidth = 8; // 8-bit bus
size_t transactionQueueDepth = 10;
size_t bufferSize = 0; // Will be calculated if 0
// LCD command/parameter configuration
int lcdCmdBits = 8;
int lcdParamBits = 8;
// DC line level configuration
struct {
bool dcIdleLevel = 0;
bool dcCmdLevel = 0;
bool dcDummyLevel = 0;
bool dcDataLevel = 1;
} dcLevels;
// Bus flags
struct {
bool csActiveHigh = false;
bool reverseColorBits = false;
bool swapColorBytes = true;
bool pclkActiveNeg = false;
bool pclkIdleLow = false;
} flags;
// Display configuration
bool swapXY = false;
bool mirrorX = false;
bool mirrorY = false;
bool invertColor = true;
// Additional features
std::shared_ptr<tt::hal::touch::TouchDevice> touch = nullptr;
std::function<void(uint8_t)> backlightDutyFunction = nullptr;
// Basic constructor - requires resolution to be set separately
Configuration(gpio_num_t cs, gpio_num_t dc, gpio_num_t wr,
std::array<gpio_num_t, 8> data, gpio_num_t rst, gpio_num_t bl)
: csPin(cs), dcPin(dc), wrPin(wr), dataPins(data), resetPin(rst), backlightPin(bl),
horizontalResolution(0), verticalResolution(0) {} // Initialize to 0
// Method to calculate buffer size after resolution is set
void calculateBufferSize() {
if (bufferSize == 0 && horizontalResolution > 0 && verticalResolution > 0) {
bufferSize = horizontalResolution * verticalResolution / 10;
}
}
// Validation method
bool isValid() const {
return horizontalResolution > 0 && verticalResolution > 0;
}
};
private:
Configuration configuration;
esp_lcd_i80_bus_handle_t i80BusHandle = nullptr;
esp_lcd_panel_io_handle_t ioHandle = nullptr;
esp_lcd_panel_handle_t panelHandle = nullptr;
lv_display_t* lvglDisplay = nullptr;
std::shared_ptr<std::mutex> lock;
uint8_t* displayBuffer = nullptr;
// Internal initialization methods
bool createI80Bus();
bool createPanelIO();
bool createPanel();
public:
explicit St7796i8080Display(const Configuration& config);
lv_display_t* getLvglDisplay() const override { return lvglDisplay; }
std::string getName() const override { return "I8080 ST7796"; }
std::string getDescription() const override { return "I8080-based ST7796 display"; }
// Lifecycle
bool start() override;
bool stop() override;
bool startLvgl() override;
bool stopLvgl() override;
// Capabilities
bool supportsLvgl() const override { return true; }
bool supportsDisplayDriver() const override { return false; }
bool supportsBacklightDuty() const override { return configuration.backlightDutyFunction != nullptr; }
// Touch and backlight
std::shared_ptr<tt::hal::touch::TouchDevice> getTouchDevice() override { return configuration.touch; }
std::shared_ptr<tt::hal::display::DisplayDriver> getDisplayDriver() override { return nullptr; }
void setBacklightDuty(uint8_t backlightDuty) override {
if (configuration.backlightDutyFunction != nullptr) {
configuration.backlightDutyFunction(backlightDuty);
}
}
// Hardware access methods
esp_lcd_panel_io_handle_t getIoHandle() const { return ioHandle; }
esp_lcd_panel_handle_t getPanelHandle() const { return panelHandle; }
// Resolution access methods
uint16_t getHorizontalResolution() const { return configuration.horizontalResolution; }
uint16_t getVerticalResolution() const { return configuration.verticalResolution; }
};
// Factory function for registration
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();