From 1805000e9aa407798822eb14284913621894e896 Mon Sep 17 00:00:00 2001 From: Anton Viktorov Date: Wed, 25 Dec 2024 19:40:36 +0100 Subject: [PATCH] initial cst328 --- esphome/components/cst328/__init__.py | 6 + .../components/cst328/touchscreen/__init__.py | 36 +++++ .../cst328/touchscreen/cst328_touchscreen.cpp | 143 ++++++++++++++++++ .../cst328/touchscreen/cst328_touchscreen.h | 76 ++++++++++ 4 files changed, 261 insertions(+) create mode 100644 esphome/components/cst328/__init__.py create mode 100644 esphome/components/cst328/touchscreen/__init__.py create mode 100644 esphome/components/cst328/touchscreen/cst328_touchscreen.cpp create mode 100644 esphome/components/cst328/touchscreen/cst328_touchscreen.h diff --git a/esphome/components/cst328/__init__.py b/esphome/components/cst328/__init__.py new file mode 100644 index 0000000000..374df64898 --- /dev/null +++ b/esphome/components/cst328/__init__.py @@ -0,0 +1,6 @@ +import esphome.codegen as cg + +CODEOWNERS = ["@latonita"] +DEPENDENCIES = ["i2c"] + +cst328_ns = cg.esphome_ns.namespace("cst328") diff --git a/esphome/components/cst328/touchscreen/__init__.py b/esphome/components/cst328/touchscreen/__init__.py new file mode 100644 index 0000000000..5992d714f2 --- /dev/null +++ b/esphome/components/cst328/touchscreen/__init__.py @@ -0,0 +1,36 @@ +from esphome import pins +import esphome.codegen as cg +from esphome.components import i2c, touchscreen +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN + +from .. import cst328_ns + +CST328Touchscreen = cst328_ns.class_( + "CST328Touchscreen", + touchscreen.Touchscreen, + i2c.I2CDevice, +) + +CONFIG_SCHEMA = ( + touchscreen.touchscreen_schema("100ms") + .extend( + { + cv.GenerateID(): cv.declare_id(CST328Touchscreen), + cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(i2c.i2c_device_schema(0x1A)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await touchscreen.register_touchscreen(var, config) + await i2c.register_i2c_device(var, config) + + if interrupt_pin := config.get(CONF_INTERRUPT_PIN): + cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin))) + if reset_pin := config.get(CONF_RESET_PIN): + cg.add(var.set_reset_pin(await cg.gpio_pin_expression(reset_pin))) diff --git a/esphome/components/cst328/touchscreen/cst328_touchscreen.cpp b/esphome/components/cst328/touchscreen/cst328_touchscreen.cpp new file mode 100644 index 0000000000..c838ad1166 --- /dev/null +++ b/esphome/components/cst328/touchscreen/cst328_touchscreen.cpp @@ -0,0 +1,143 @@ +#include "cst328_touchscreen.h" + +namespace esphome { +namespace cst328 { + +void CST328Touchscreen::setup() { + ESP_LOGCONFIG(TAG, "Setting up CST328 Touchscreen..."); + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + this->reset_pin_->digital_write(true); + delay(50); + this->reset_pin_->digital_write(false); + delay(5); + this->reset_pin_->digital_write(true); + this->set_timeout(50, [this] { this->continue_setup_(); }); + } else { + this->continue_setup_(); + } +} + +void CST328Touchscreen::continue_setup_() { + uint8_t buffer[4]; + + if (this->interrupt_pin_ != nullptr) { + this->interrupt_pin_->setup(); + this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); + } + + // buffer[0] = 0xD1; + // if (this->write_register16(0xD1, buffer, 1) != i2c::ERROR_OK) { + // ESP_LOGE(TAG, "Write byte to 0xD1 failed"); + // this->mark_failed(); + // return; + // } + // delay(10); + + // Enter debug/info mode + if (this->write_register16(static_cast(Cst328WorkModes::DEBUG_INFO_MODE), buffer, 0) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "Failed to enter debug/info mode"); + this->mark_failed(); + return; + } + + // Read chip and project ID + if (this->read16_(static_cast(Cst328Registers::CHIP_TYPE_AND_PROJECT_ID), buffer, 4)) { + this->chip_id_ = buffer[2] + (buffer[3] << 8); + this->project_id_ = buffer[0] + (buffer[1] << 8); + ESP_LOGV(TAG, "Chip ID %X, project ID %X", this->chip_id_, this->project_id_); + } + + // Read FW checksum + if (this->read16_(static_cast(Cst328Registers::FW_CRC_AND_BOOT_TIME), buffer, 4)) { + uint16_t fw_crc = buffer[2] + (buffer[3] << 8); + if (0xCACA != fw_crc) { + ESP_LOGE(TAG, "Invalid FW checksum: %X", fw_crc); + this->mark_failed(); + return; + } + } + + // Read FW version + if (this->read16_(static_cast(Cst328Registers::FW_REVISION), buffer, 4)) { + this->fw_ver_major_ = buffer[3]; + this->fw_ver_minor_ = buffer[2]; + this->fw_build_ = buffer[0] + (buffer[1] << 8); + ESP_LOGV(TAG, "FW version %d.%d.%d", this->fw_ver_major_, this->fw_ver_minor_, this->fw_build_); + } + + // Read X/Y resolution + if (this->read16_(static_cast(Cst328Registers::X_Y_RESOLUTION), buffer, 4)) { + this->x_raw_max_ = buffer[0] + (buffer[1] << 8); + this->y_raw_max_ = buffer[2] + (buffer[3] << 8); + } else { + this->x_raw_max_ = this->display_->get_native_width(); + this->y_raw_max_ = this->display_->get_native_height(); + } + + // Enter normal mode + this->write_register16(static_cast(Cst328WorkModes::NORMAL_MODE), buffer, 0); + + this->setup_complete_ = true; + + ESP_LOGV(TAG, "CST328 Touchscreen setup complete"); +} + +void CST328Touchscreen::dump_config() { + ESP_LOGCONFIG(TAG, "CST328 Touchscreen:"); + LOG_I2C_DEVICE(this); + LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + ESP_LOGCONFIG(TAG, " Chip ID: 0x%04X, Project ID: 0x%04X", this->chip_id_, this->project_id_); + ESP_LOGCONFIG(TAG, " FW version: %d.%d.%d", this->fw_ver_major_, this->fw_ver_minor_, this->fw_build_); + ESP_LOGCONFIG(TAG, " X/Y resolution: %d/%d", this->x_raw_max_, this->y_raw_max_); +} + +void CST328Touchscreen::update_touches() { + uint8_t data[CST328_TOUCH_DATA_SIZE]; + uint8_t clear{0}; + uint8_t touch_cnt = 0; + this->skip_update_ = true; + if (!this->read_register16(static_cast(Cst328Registers::TOUCH_FINGER_NUMBER), data, 1)) { + this->status_set_warning(); + return; + } + this->status_clear_warning(); + + // number of fingers + touch_cnt = data[0] & 0x0F; + + // no touch or error + if (touch_cnt == 0 || touch_cnt > CST328_TOUCH_MAX_POINTS) { + // clear touch + this->write_register16(static_cast(Cst328Registers::TOUCH_FINGER_NUMBER), &clear, 1); + return; + } + + // read all points + if (!this->read_register16(static_cast(Cst328Registers::TOUCH_INFORMATION), data, sizeof(data))) { + ESP_LOGV(TAG, "Failed to read touch data"); + this->status_set_warning(); + return; + } + + // clear touch + this->write_register16(static_cast(Cst328Registers::TOUCH_FINGER_NUMBER), &clear, 1); + + this->skip_update_ = false; + size_t index = 0; + for (uint8_t i = 0; i != touch_cnt; i++) { + uint8_t id = data[index] >> 4; + int16_t x = (data[index + 1] << 4) | ((data[index + 3] >> 4) & 0x0F); + int16_t y = (data[index + 2] << 4) | (data[index + 3] & 0x0F); + int16_t z = data[index + 4]; + this->add_raw_touch_position_(id, x, y, z); + ESP_LOGV(TAG, "Read touch %d: %d/%d", id, x, y); + index += 5; + if (i == 0) { + index += 2; + } + } +} +} // namespace cst328 +} // namespace esphome diff --git a/esphome/components/cst328/touchscreen/cst328_touchscreen.h b/esphome/components/cst328/touchscreen/cst328_touchscreen.h new file mode 100644 index 0000000000..6fc64b41f2 --- /dev/null +++ b/esphome/components/cst328/touchscreen/cst328_touchscreen.h @@ -0,0 +1,76 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/touchscreen/touchscreen.h" +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace cst328 { + +static const char *const TAG = "cst328.touchscreen"; + +static const uint8_t CST328_REG_STATUS = 0x00; +static const uint8_t CST328_TOUCH_MAX_POINTS = 5; +static const uint8_t CST328_TOUCH_DATA_SIZE = 27; + +enum class Cst328Registers : uint16_t { + TOUCH_INFORMATION = 0xD000, + TOUCH_FINGER_NUMBER = 0xD005, + KEY_TX_RX_NUMBERS = 0xD1F4, + X_Y_RESOLUTION = 0xD1F8, + FW_CRC_AND_BOOT_TIME = 0xD1FC, + CHIP_TYPE_AND_PROJECT_ID = 0xD204, + FW_REVISION = 0xD208, +}; + +enum class Cst328WorkModes : uint16_t { + DEBUG_INFO_MODE = 0xD101, + RESET_MODE = 0xD102, + RECALIBRATION_MODE = 0xD104, + DEEP_SLEEP_MODE = 0xD105, + DEBUG_POINT_MODE = 0xD108, + NORMAL_MODE = 0xD109, + DEBUG_RAWDATA_MODE = 0xD10A, + DEBUG_DIFF_MODE = 0xD10D, + DEBUG_FACTORY_MODE = 0xD119, + DEBUG_FACTORY_MODE_2 = 0xD120, +}; + +class CST328Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice { + public: + void setup() override; + void update_touches() override; + void dump_config() override; + + void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; } + void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; } + + bool can_proceed() override { return this->setup_complete_ || this->is_failed(); } + + protected: + bool read16_(uint16_t addr, uint8_t *data, size_t len) { + if (this->read_register16(addr, data, len) != i2c::ERROR_OK) { + esph_log_e(TAG, "Read data from 0x%04X failed", addr); + this->mark_failed(); + return false; + } + return true; + } + void continue_setup_(); + + InternalGPIOPin *interrupt_pin_{}; + GPIOPin *reset_pin_{}; + + uint16_t chip_id_{}; + uint16_t project_id_{}; + uint8_t fw_ver_major_{}; + uint8_t fw_ver_minor_{}; + uint16_t fw_build_{}; + + bool setup_complete_{}; +}; + +} // namespace cst328 +} // namespace esphome