From 7836adb8007752120f019e2063b339c6e8dc793e Mon Sep 17 00:00:00 2001 From: Clive Blackledge Date: Tue, 9 Jun 2026 23:30:59 -0700 Subject: [PATCH] Add T-Deck Max variant --- src/AudioThread.h | 12 + src/Power.cpp | 29 +- src/detect/ScanI2CTwoWire.cpp | 9 +- src/graphics/EInkDisplay2.cpp | 19 + src/graphics/EInkDisplay2.h | 4 +- src/graphics/Screen.cpp | 10 +- src/graphics/draw/MenuHandler.cpp | 2 + src/input/TDeckProKeyboard.cpp | 4 +- src/input/TouchScreenBase.cpp | 2 +- src/input/kbI2cBase.cpp | 6 +- src/mesh/generated/meshtastic/mesh.pb.h | 2 + src/platform/esp32/architecture.h | 4 +- src/platform/esp32/main-esp32.cpp | 16 + .../extra_variants/t_deck_max/variant.cpp | 120 ++++++ src/sleep.cpp | 14 +- .../t-deck-max/HynTouch/src/HynTouch.cpp | 376 ++++++++++++++++++ .../t-deck-max/HynTouch/src/HynTouch.h | 40 ++ .../esp32s3/t-deck-max/HynTouch/src/hyn_cfg.h | 23 ++ .../t-deck-max/HynTouch/src/hyn_core.h | 239 +++++++++++ .../t-deck-max/HynTouch/src/hyn_cst226se.c | 248 ++++++++++++ .../t-deck-max/HynTouch/src/hyn_cst3xx.c | 219 ++++++++++ .../t-deck-max/HynTouch/src/hyn_cst66xx.c | 274 +++++++++++++ .../esp32s3/t-deck-max/HynTouch/src/hyn_i2c.c | 87 ++++ .../t-deck-max/HynTouch/src/hyn_platform.cpp | 90 +++++ .../t-deck-max/HynTouch/src/hyn_platform.h | 17 + .../t-deck-max/HynTouch/src/hyn_ts_ext.c | 75 ++++ variants/esp32s3/t-deck-max/TDeckMaxBoard.h | 139 +++++++ variants/esp32s3/t-deck-max/pins_arduino.h | 17 + variants/esp32s3/t-deck-max/platformio.ini | 50 +++ variants/esp32s3/t-deck-max/variant.cpp | 45 +++ variants/esp32s3/t-deck-max/variant.h | 118 ++++++ 31 files changed, 2293 insertions(+), 17 deletions(-) create mode 100644 src/platform/extra_variants/t_deck_max/variant.cpp create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/HynTouch.cpp create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/HynTouch.h create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/hyn_cfg.h create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/hyn_core.h create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst226se.c create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst3xx.c create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst66xx.c create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/hyn_i2c.c create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/hyn_platform.cpp create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/hyn_platform.h create mode 100644 variants/esp32s3/t-deck-max/HynTouch/src/hyn_ts_ext.c create mode 100644 variants/esp32s3/t-deck-max/TDeckMaxBoard.h create mode 100644 variants/esp32s3/t-deck-max/pins_arduino.h create mode 100644 variants/esp32s3/t-deck-max/platformio.ini create mode 100644 variants/esp32s3/t-deck-max/variant.cpp create mode 100644 variants/esp32s3/t-deck-max/variant.h diff --git a/src/AudioThread.h b/src/AudioThread.h index 1129ee087ec..66b41e719e5 100644 --- a/src/AudioThread.h +++ b/src/AudioThread.h @@ -17,6 +17,10 @@ extern ExtensionIOXL9555 io; #endif +#ifdef T_DECK_MAX +void tDeckMaxSetAudioAmp(bool enable); +#endif + #define AUDIO_THREAD_INTERVAL_MS 100 class AudioThread : public concurrency::OSThread @@ -28,6 +32,8 @@ class AudioThread : public concurrency::OSThread { #ifdef T_LORA_PAGER io.digitalWrite(EXPANDS_AMP_EN, HIGH); +#elif defined(T_DECK_MAX) + tDeckMaxSetAudioAmp(true); #endif setCPUFast(true); rtttlFile = std::unique_ptr(new AudioFileSourcePROGMEM(data, len)); @@ -56,6 +62,8 @@ class AudioThread : public concurrency::OSThread setCPUFast(false); #ifdef T_LORA_PAGER io.digitalWrite(EXPANDS_AMP_EN, LOW); +#elif defined(T_DECK_MAX) + tDeckMaxSetAudioAmp(false); #endif } @@ -68,12 +76,16 @@ class AudioThread : public concurrency::OSThread #ifdef T_LORA_PAGER io.digitalWrite(EXPANDS_AMP_EN, HIGH); +#elif defined(T_DECK_MAX) + tDeckMaxSetAudioAmp(true); #endif auto sam = std::unique_ptr(new ESP8266SAM); sam->Say(audioOut.get(), text); setCPUFast(false); #ifdef T_LORA_PAGER io.digitalWrite(EXPANDS_AMP_EN, LOW); +#elif defined(T_DECK_MAX) + tDeckMaxSetAudioAmp(false); #endif } diff --git a/src/Power.cpp b/src/Power.cpp index 42bdf3ce4c5..4e261e1ecb4 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -1640,9 +1640,12 @@ bool Power::cw2015Init() #endif #if defined(HAS_PPM) && HAS_PPM +#if defined(XPOWERS_CHIP_SY6970) && !defined(SY6970_SLAVE_ADDRESS) +#define SY6970_SLAVE_ADDRESS 0x6A +#endif /** - * Adapter class for BQ25896/BQ27220 Lipo battery charger. + * Adapter class for XPowers charger/BQ27220 Lipo battery reporting. */ class LipoCharger : public HasBatteryLevel { @@ -1650,13 +1653,30 @@ class LipoCharger : public HasBatteryLevel BQ27220 *bq = nullptr; public: - /** - * Init the I2C BQ25896 Lipo battery charger - */ bool runOnce() { if (PPM == nullptr) { PPM = new XPowersPPM; +#if defined(XPOWERS_CHIP_SY6970) + bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, SY6970_SLAVE_ADDRESS); + if (result) { + LOG_INFO("PPM SY6970 init succeeded"); + + PPM->setSysPowerDownVoltage(3300); + PPM->setInputCurrentLimit(3250); + PPM->disableCurrentLimitPin(); + PPM->setChargeTargetVoltage(4208); + PPM->setPrechargeCurr(64); + PPM->setChargerConstantCurr(1024); + PPM->enableMeasure(); + PPM->enableCharge(); + } else { + LOG_WARN("PPM SY6970 init failed"); + delete PPM; + PPM = nullptr; + return false; + } +#else bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR); if (result) { LOG_INFO("PPM BQ25896 init succeeded"); @@ -1693,6 +1713,7 @@ class LipoCharger : public HasBatteryLevel PPM = nullptr; return false; } +#endif } if (bq == nullptr) { bq = new BQ27220; diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index a3bdf14407d..356e8cd1653 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -633,7 +633,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; - SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address); + case LSM6DS3_ADDR: +#ifdef T_DECK_MAX + LOG_INFO("SY6970 charger found at address 0x%x", (uint8_t)addr.address); +#else + type = LSM6DS3; + logFoundDevice("LSM6DS3", (uint8_t)addr.address); +#endif + break; SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address); case TCA9555_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x01), 1); diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index 28b956bb104..e7f7dace243 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -243,6 +243,25 @@ bool EInkDisplay::connect() adafruitDisplay->setRotation(0); adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); } +#elif defined(T_DECK_MAX) + pinMode(PIN_EINK_CS, OUTPUT); + digitalWrite(PIN_EINK_CS, HIGH); +#ifdef SDCARD_CS + pinMode(SDCARD_CS, OUTPUT); + digitalWrite(SDCARD_CS, HIGH); +#endif +#ifdef LORA_CS + pinMode(LORA_CS, OUTPUT); + digitalWrite(LORA_CS, HIGH); +#endif + + LOG_INFO("T-Deck Max EPD init cs=%d dc=%d rst=%d busy=%d sck=%d mosi=%d", PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, + PIN_EINK_BUSY, PIN_EINK_SCLK, PIN_EINK_MOSI); + auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); + adafruitDisplay = new GxEPD2_BW(*lowLevel); + adafruitDisplay->init(115200, true, 2, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0)); + adafruitDisplay->setRotation(0); + adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); #elif defined(M5_COREINK) || defined(T_DECK_PRO) auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); adafruitDisplay = new GxEPD2_BW(*lowLevel); diff --git a/src/graphics/EInkDisplay2.h b/src/graphics/EInkDisplay2.h index 645a3f2d082..61cf85da9e3 100644 --- a/src/graphics/EInkDisplay2.h +++ b/src/graphics/EInkDisplay2.h @@ -9,9 +9,9 @@ #include "GxEPD2Multi.h" #endif -// Limit how often we push a full E-Ink refresh. T-Deck Pro needs faster updates for typing. +// Limit how often we push a full E-Ink refresh. T-Deck Pro/Max need faster updates for typing. #ifndef EINK_FORCE_DISPLAY_THROTTLE_MS -#if defined(T_DECK_PRO) +#if defined(T_DECK_PRO) || defined(T_DECK_MAX) #define EINK_FORCE_DISPLAY_THROTTLE_MS 200 #else #define EINK_FORCE_DISPLAY_THROTTLE_MS 1000 diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 81befe329f9..90845935b2d 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -83,7 +83,7 @@ extern MessageStore messageStore; #include "platform/portduino/PortduinoGlue.h" #endif -#if defined(T_LORA_PAGER) +#if defined(T_LORA_PAGER) || defined(T_DECK_MAX) // KB backlight control #include "input/cardKbI2cImpl.h" #endif @@ -541,6 +541,8 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) #ifdef PIN_EINK_EN if (uiconfig.screen_brightness == 1) digitalWrite(PIN_EINK_EN, HIGH); +#elif defined(HAS_EINK_FRONTLIGHT) && defined(PIN_EINK_BL) + analogWrite(PIN_EINK_BL, brightness); #elif defined(PCA_PIN_EINK_EN) if (uiconfig.screen_brightness > 0) io.digitalWrite(PCA_PIN_EINK_EN, HIGH); @@ -585,6 +587,8 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) #ifdef PIN_EINK_EN digitalWrite(PIN_EINK_EN, LOW); +#elif defined(HAS_EINK_FRONTLIGHT) && defined(PIN_EINK_BL) + analogWrite(PIN_EINK_BL, 0); #elif defined(PCA_PIN_EINK_EN) io.digitalWrite(PCA_PIN_EINK_EN, LOW); #endif @@ -697,6 +701,8 @@ void Screen::setup() // Apply loaded brightness #if defined(ST7789_CS) static_cast(dispdev)->setDisplayBrightness(brightness); +#elif defined(HAS_EINK_FRONTLIGHT) && defined(PIN_EINK_BL) + analogWrite(PIN_EINK_BL, brightness); #elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SPISSD1306) dispdev->setBrightness(brightness); #endif @@ -805,7 +811,7 @@ void Screen::setup() void Screen::setOn(bool on, FrameCallback einkScreensaver) { -#if defined(T_LORA_PAGER) +#if defined(T_LORA_PAGER) || defined(T_DECK_MAX) if (cardKbI2cImpl) cardKbI2cImpl->toggleBacklight(on); #endif diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 9157dc31db5..8bfb547ea5c 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -2131,6 +2131,8 @@ void menuHandler::BrightnessPickerMenu() #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) // For HELTEC devices, use analogWrite to control backlight analogWrite(VTFT_LEDA, uiconfig.screen_brightness); +#elif defined(HAS_EINK_FRONTLIGHT) && defined(PIN_EINK_BL) + analogWrite(PIN_EINK_BL, uiconfig.screen_brightness); #elif defined(ST7789_CS) || defined(ST7796_CS) static_cast(screen->getDisplayDevice())->setDisplayBrightness(uiconfig.screen_brightness); #elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) diff --git a/src/input/TDeckProKeyboard.cpp b/src/input/TDeckProKeyboard.cpp index b83f0c6aed8..419eb54ccfb 100644 --- a/src/input/TDeckProKeyboard.cpp +++ b/src/input/TDeckProKeyboard.cpp @@ -1,4 +1,4 @@ -#if defined(T_DECK_PRO) +#if defined(T_DECK_PRO) || defined(T_DECK_MAX) #include "TDeckProKeyboard.h" @@ -192,4 +192,4 @@ bool TDeckProKeyboard::isModifierKey(uint8_t key) return (key == modifierRightShiftKey || key == modifierLeftShiftKey || key == modifierAltKey || key == modifierSymKey); } -#endif // T_DECK_PRO \ No newline at end of file +#endif // T_DECK_PRO || T_DECK_MAX diff --git a/src/input/TouchScreenBase.cpp b/src/input/TouchScreenBase.cpp index a3d03d4acf5..11f9c45d8b4 100644 --- a/src/input/TouchScreenBase.cpp +++ b/src/input/TouchScreenBase.cpp @@ -186,7 +186,7 @@ int32_t TouchScreenBase::runOnce() void TouchScreenBase::hapticFeedback() { -#ifdef T_WATCH_S3 +#if defined(T_WATCH_S3) || defined(T_DECK_MAX) drv.setWaveform(0, 75); drv.setWaveform(1, 0); // end waveform drv.go(); diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index 510fb1e31df..700809ed518 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -3,7 +3,7 @@ #include "detect/ScanI2C.h" #include "detect/ScanI2CTwoWire.h" -#if defined(T_DECK_PRO) +#if defined(T_DECK_PRO) || defined(T_DECK_MAX) #include "TDeckProKeyboard.h" #elif defined(T_LORA_PAGER) #include "TLoraPagerKeyboard.h" @@ -20,7 +20,7 @@ extern uint8_t kb_model; KbI2cBase::KbI2cBase(const char *name) : concurrency::OSThread(name), -#if defined(T_DECK_PRO) +#if defined(T_DECK_PRO) || defined(T_DECK_MAX) TCAKeyboard(*(new TDeckProKeyboard())) #elif defined(T_LORA_PAGER) TCAKeyboard(*(new TLoraPagerKeyboard())) @@ -550,7 +550,7 @@ int32_t KbI2cBase::runOnce() void KbI2cBase::toggleBacklight(bool on) { -#if defined(T_LORA_PAGER) +#if defined(T_LORA_PAGER) || defined(T_DECK_MAX) TCAKeyboard.setBacklight(on); #endif } diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 7c2eef04f5b..f7893189190 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -325,6 +325,8 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_T_IMPULSE_PLUS = 135, /* Lilygo T-Echo Card */ meshtastic_HardwareModel_T_ECHO_CARD = 136, + /* Lilygo T-Deck Max */ + meshtastic_HardwareModel_T_DECK_MAX = 139, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index e4ec807f823..8f41e1c39e8 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -196,6 +196,8 @@ #define HW_VENDOR meshtastic_HardwareModel_LINK_32 #elif defined(T_DECK_PRO) #define HW_VENDOR meshtastic_HardwareModel_T_DECK_PRO +#elif defined(T_DECK_MAX) +#define HW_VENDOR meshtastic_HardwareModel_T_DECK_MAX #elif defined(T_BEAM_1W) #define HW_VENDOR meshtastic_HardwareModel_TBEAM_1_WATT #elif defined(T_LORA_PAGER) @@ -242,4 +244,4 @@ // Setup flag, which indicates if our device supports dynamic light sleep #if defined(HAS_ESP32_PM_SUPPORT) && defined(CONFIG_FREERTOS_USE_TICKLESS_IDLE) #define HAS_ESP32_DYNAMIC_LIGHT_SLEEP 1 -#endif \ No newline at end of file +#endif diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 69b69ff686e..0e436f47f67 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -249,6 +249,22 @@ void cpuDeepSleep(uint32_t msecToWake) #endif #ifdef BUTTON_NEED_PULLUP gpio_pullup_en((gpio_num_t)BUTTON_PIN); +#endif +#if defined(T_DECK_MAX) && SOC_RTCIO_HOLD_SUPPORTED && SOC_PM_SUPPORT_EXT_WAKEUP +#ifdef KB_INT + if (rtc_gpio_is_valid_gpio((gpio_num_t)KB_INT)) { + gpioMask |= (1ULL << KB_INT); + gpio_pullup_en((gpio_num_t)KB_INT); + LOG_INFO("setup KB_INT (GPIO%02d) for deep sleep wake", KB_INT); + } +#endif +#if defined(WAKE_ON_TOUCH) && defined(SCREEN_TOUCH_INT) + if (rtc_gpio_is_valid_gpio((gpio_num_t)SCREEN_TOUCH_INT)) { + gpioMask |= (1ULL << SCREEN_TOUCH_INT); + gpio_pullup_en((gpio_num_t)SCREEN_TOUCH_INT); + LOG_INFO("setup TOUCH_INT (GPIO%02d) for deep sleep wake", SCREEN_TOUCH_INT); + } +#endif #endif // Not needed because both of the current boards have external pullups diff --git a/src/platform/extra_variants/t_deck_max/variant.cpp b/src/platform/extra_variants/t_deck_max/variant.cpp new file mode 100644 index 00000000000..d39171891ae --- /dev/null +++ b/src/platform/extra_variants/t_deck_max/variant.cpp @@ -0,0 +1,120 @@ +#include "configuration.h" + +#ifdef T_DECK_MAX + +#include "AudioBoard.h" +#include "ExtensionIOXL9555.hpp" +#include "HynTouch.h" +#include "input/InputBroker.h" +#include "input/TouchScreenImpl1.h" +#include "sleep.h" +#include + +extern ExtensionIOXL9555 io; + +DriverPins PinsAudioBoardES8311; +AudioBoard board(AudioDriverES8311, PinsAudioBoardES8311); + +static bool readTouch(int16_t *x, int16_t *y) +{ + int16_t xArray[1] = {0}; + int16_t yArray[1] = {0}; + + if (hyn_touch_get_point(xArray, yArray, 1) == 0) { + return false; + } + + *x = xArray[0]; + *y = yArray[0]; + return true; +} + +static input_broker_event touchKeyToEvent(uint8_t keyId) +{ + // Vendor firmware reports the three bezel keys left-to-right as heart, circle, paper airplane. + switch (keyId) { + case 0: + return INPUT_BROKER_USER_PRESS; + case 1: + return INPUT_BROKER_SELECT; + case 2: + return INPUT_BROKER_SEND_PING; + default: + return INPUT_BROKER_NONE; + } +} + +static void touchKeyCallback(uint8_t keyId, bool pressed, void *userData) +{ + (void)userData; + if (!pressed || !inputBroker) { + return; + } + + InputEvent event = { + .source = "touchkeys", + .inputEvent = touchKeyToEvent(keyId), + .kbchar = 0, + .touchX = 0, + .touchY = 0, + }; + if (event.inputEvent != INPUT_BROKER_NONE) { + inputBroker->injectInputEvent(&event); + LOG_DEBUG("T-Deck Max touch key %u pressed", keyId); + } +} + +#ifdef ARCH_ESP32 +struct TDeckMaxTouchLightSleepObserver { + int onLightSleep(void *) + { + hyn_touch_before_light_sleep(); + return 0; + } + + int onLightSleepEnd(esp_sleep_wakeup_cause_t) + { + hyn_touch_after_light_sleep(); + return 0; + } + + CallbackObserver sleepObserver{this, &TDeckMaxTouchLightSleepObserver::onLightSleep}; + CallbackObserver wakeObserver{ + this, &TDeckMaxTouchLightSleepObserver::onLightSleepEnd}; +} static touchLightSleepObserver; +#endif + +void tDeckMaxSetAudioAmp(bool enable) +{ + io.digitalWrite(EXPANDS_AUDIO_SEL, LOW); + io.digitalWrite(EXPANDS_AMP_EN, enable ? HIGH : LOW); +} + +void lateInitVariant() +{ + hyn_touch_attach_xl9555(&io); + hyn_touch_set_key_callback(touchKeyCallback, nullptr); + if (hyn_touch_init()) { +#ifdef ARCH_ESP32 + touchLightSleepObserver.sleepObserver.observe(¬ifyLightSleep); + touchLightSleepObserver.wakeObserver.observe(¬ifyLightSleepEnd); +#endif + touchScreenImpl1 = new TouchScreenImpl1(EINK_WIDTH, EINK_HEIGHT, readTouch); + touchScreenImpl1->init(); + } else { + LOG_WARN("T-Deck Max touch init failed"); + } + + PinsAudioBoardES8311.addI2C(PinFunction::CODEC, Wire); + PinsAudioBoardES8311.addI2S(PinFunction::CODEC, DAC_I2S_MCLK, DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_DIN); + + CodecConfig cfg; + cfg.input_device = ADC_INPUT_LINE1; + cfg.output_device = DAC_OUTPUT_ALL; + cfg.i2s.bits = BIT_LENGTH_16BITS; + cfg.i2s.rate = RATE_44K; + board.begin(cfg); + board.setVolume(75); +} + +#endif diff --git a/src/sleep.cpp b/src/sleep.cpp index 626fc4e11bd..2e3df08fd15 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -36,6 +36,10 @@ esp_sleep_source_t wakeCause; // the reason we booted this time extern ExtensionIOXL9555 io; #endif +#ifdef T_DECK_MAX +#include "HynTouch.h" +#endif + #ifdef HAS_PPM #include extern XPowersPPM *PPM; @@ -249,6 +253,10 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN if (screen) screen->doDeepSleep(); // datasheet says this will draw only 10ua +#if defined(T_DECK_MAX) && defined(HAS_EINK_FRONTLIGHT) && defined(PIN_EINK_BL) + analogWrite(PIN_EINK_BL, 0); +#endif + if (!skipSaveNodeDb) { nodeDB->saveToDisk(); } @@ -330,10 +338,14 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN #ifdef HAS_PPM if (PPM) { - // BQ25896 PMIC shutdown is a hard power-off state. + // PPM charger shutdown is a hard power-off state. // Only use it for "sleep forever" / explicit shutdown, because timed deep sleep // must remain wakeable by RTC timer. if (msecToWake == portMAX_DELAY) { +#ifdef T_DECK_MAX + hyn_sleep(); + io.digitalWrite(EXPANDS_TOUCH_RST, LOW); +#endif LOG_INFO("PPM shutdown"); console->flush(); PPM->shutdown(); diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/HynTouch.cpp b/variants/esp32s3/t-deck-max/HynTouch/src/HynTouch.cpp new file mode 100644 index 00000000000..879a8d088d8 --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/HynTouch.cpp @@ -0,0 +1,376 @@ +#include "HynTouch.h" + +#include +#include +#include + +#include + +#include + +#include "hyn_core.h" + +#ifndef HYN_TOUCH_RUNTIME_LOG +#define HYN_TOUCH_RUNTIME_LOG 0 +#endif + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +namespace +{ + +constexpr const char *kTag = "HynTouch"; + +struct HynTouchRuntime { + HynTouchConfig config = {}; + struct hyn_ts_data *data = nullptr; + volatile bool press_flag = false; + bool ready = false; + bool isr_attached = false; + int attached_irq = -1; + bool key_pressed[3] = {false, false, false}; + bool key_seen[3] = {false, false, false}; + HynTouchKeyCallback key_callback = nullptr; + void *key_callback_user_data = nullptr; +}; + +HynTouchRuntime g_touch; + +void clear_key_state() +{ + memset(g_touch.key_pressed, 0, sizeof(g_touch.key_pressed)); + memset(g_touch.key_seen, 0, sizeof(g_touch.key_seen)); +} + +void apply_axis_transform(uint8_t point_count) +{ + if (!g_touch.data) { + return; + } + + for (u8 i = 0; i < point_count; ++i) { + if (g_touch.data->plat_data.swap_xy) { + u16 tmp = g_touch.data->rp_buf.pos_info[i].pos_x; + g_touch.data->rp_buf.pos_info[i].pos_x = g_touch.data->rp_buf.pos_info[i].pos_y; + g_touch.data->rp_buf.pos_info[i].pos_y = tmp; + } + if (g_touch.data->plat_data.reverse_x) { + g_touch.data->rp_buf.pos_info[i].pos_x = + g_touch.data->plat_data.x_resolution - g_touch.data->rp_buf.pos_info[i].pos_x; + } + if (g_touch.data->plat_data.reverse_y) { + g_touch.data->rp_buf.pos_info[i].pos_y = + g_touch.data->plat_data.y_resolution - g_touch.data->rp_buf.pos_info[i].pos_y; + } + } +} + +void handle_key_report() +{ + if (!g_touch.data || (g_touch.data->rp_buf.report_need & REPORT_KEY) == 0) { + return; + } + + const int key_id = g_touch.data->rp_buf.key_id; + if (key_id < 0 || key_id >= 3) { + return; + } + + const bool pressed = g_touch.data->rp_buf.key_state == 1; + if (pressed) { + g_touch.key_seen[key_id] = true; + } + + if (g_touch.key_pressed[key_id] == pressed) { + return; + } + + g_touch.key_pressed[key_id] = pressed; + if (g_touch.key_callback) { + g_touch.key_callback((uint8_t)key_id, pressed, g_touch.key_callback_user_data); + } +} + +void IRAM_ATTR gpio_isr_handler(void *arg) +{ + (void)arg; + g_touch.press_flag = true; +} + +bool configure_irq_gpio(int irq_pin) +{ + if (irq_pin < 0) { + ESP_LOGE(kTag, "Invalid touch IRQ pin: %d", irq_pin); + return false; + } + + gpio_config_t io_conf = {}; + io_conf.intr_type = GPIO_INTR_NEGEDGE; + io_conf.pin_bit_mask = (1ULL << irq_pin); + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + if (gpio_config(&io_conf) != ESP_OK) { + ESP_LOGE(kTag, "Failed to configure touch IRQ pin %d", irq_pin); + return false; + } + + esp_err_t isr_ret = gpio_install_isr_service(0); + if (isr_ret != ESP_OK && isr_ret != ESP_ERR_INVALID_STATE) { + ESP_LOGE(kTag, "gpio_install_isr_service failed: %s", esp_err_to_name(isr_ret)); + return false; + } + + if (g_touch.isr_attached && g_touch.attached_irq >= 0) { + gpio_isr_handler_remove((gpio_num_t)g_touch.attached_irq); + } + + esp_err_t handler_ret = gpio_isr_handler_add((gpio_num_t)irq_pin, gpio_isr_handler, nullptr); + if (handler_ret != ESP_OK) { + ESP_LOGE(kTag, "gpio_isr_handler_add failed: %s", esp_err_to_name(handler_ret)); + return false; + } + + g_touch.isr_attached = true; + g_touch.attached_irq = irq_pin; + return true; +} + +void detach_irq_handler() +{ + if (!g_touch.isr_attached || g_touch.attached_irq < 0) { + return; + } + + gpio_isr_handler_remove((gpio_num_t)g_touch.attached_irq); + g_touch.isr_attached = false; +} + +void attach_irq_handler() +{ + if (!g_touch.ready || g_touch.isr_attached || g_touch.attached_irq < 0) { + return; + } + + esp_err_t handler_ret = gpio_isr_handler_add((gpio_num_t)g_touch.attached_irq, gpio_isr_handler, nullptr); + if (handler_ret != ESP_OK) { + ESP_LOGE(kTag, "gpio_isr_handler_add failed after sleep: %s", esp_err_to_name(handler_ret)); + return; + } + + g_touch.isr_attached = true; +} + +bool initialize_touch_core(const HynTouchConfig &config) +{ + int ret = 0; + static struct hyn_ts_data ts_data; + memset(&ts_data, 0, sizeof(ts_data)); + + g_touch.config = config; + g_touch.data = &ts_data; + g_touch.ready = false; + g_touch.press_flag = false; + clear_key_state(); + + ESP_LOGI(kTag, HYN_DRIVER_VERSION); + + struct hyn_ts_fuc *support_touch_list[] = { + (struct hyn_ts_fuc *)&cst66xx_fuc, + (struct hyn_ts_fuc *)&cst3xx_fuc, + (struct hyn_ts_fuc *)&cst226se_fuc, + }; + + g_touch.data->hyn_fuc_used = &cst66xx_fuc; + g_touch.data->plat_data.max_touch_num = config.max_touch_points ? config.max_touch_points : MAX_POINTS_REPORT; + g_touch.data->plat_data.irq_gpio = config.irq_pin; + g_touch.data->plat_data.reset_gpio = config.reset_pin; + g_touch.data->plat_data.x_resolution = config.x_resolution; + g_touch.data->plat_data.y_resolution = config.y_resolution; + g_touch.data->plat_data.swap_xy = config.swap_xy ? 1 : 0; + g_touch.data->plat_data.reverse_x = config.reverse_x ? 1 : 0; + g_touch.data->plat_data.reverse_y = config.reverse_y ? 1 : 0; + + if (g_touch.data->plat_data.reset_gpio >= 0 && !XL9555_GPIO_IS(g_touch.data->plat_data.reset_gpio)) { + gpio_config_t io_conf = {}; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = (1ULL << g_touch.data->plat_data.reset_gpio); + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + if (gpio_config(&io_conf) != ESP_OK) { + ESP_LOGE(kTag, "Failed to configure touch reset pin %d", g_touch.data->plat_data.reset_gpio); + return false; + } + } + + esp_err_t i2c_ret = hyn_i2c_init((u8)config.sda_pin, (u8)config.scl_pin); + if (i2c_ret != ESP_OK) { + ESP_LOGE(kTag, "I2C init failed: %s", esp_err_to_name(i2c_ret)); + return false; + } + + for (size_t i = 0; i < ARRAY_SIZE(support_touch_list); ++i) { + g_touch.data->hyn_fuc_used = support_touch_list[i]; + ret = g_touch.data->hyn_fuc_used->tp_chip_init(g_touch.data); + if (ret == 0) { + ESP_LOGI(kTag, "Touch init SUCCEED"); + ESP_LOGI(kTag, "IC_info fw_project_id:%lx", g_touch.data->hw_info.fw_project_id); + ESP_LOGI(kTag, "ictype:[%lx]", g_touch.data->hw_info.fw_chip_type); + ESP_LOGI(kTag, "fw_ver:%lx", g_touch.data->hw_info.fw_ver); + break; + } + } + + if (ret != 0) { + ESP_LOGE(kTag, "Touch probe failed"); + return false; + } + + if (!configure_irq_gpio(config.irq_pin)) { + return false; + } + + g_touch.ready = true; + return true; +} + +} // namespace + +HynTouchConfig hyn_touch_default_config() +{ + HynTouchConfig config = {}; + config.sda_pin = BOARD_TOUCH_SDA; + config.scl_pin = BOARD_TOUCH_SCL; + config.reset_pin = BOARD_TOUCH_RST; + config.irq_pin = BOARD_TOUCH_INT; + config.max_touch_points = MAX_POINTS_REPORT; + config.x_resolution = LCD_HOR_SIZE; + config.y_resolution = LCD_VER_SIZE; + config.swap_xy = false; + config.reverse_x = false; + config.reverse_y = false; + return config; +} + +bool hyn_touch_init() +{ + const HynTouchConfig config = hyn_touch_default_config(); + return hyn_touch_init_with_config(&config); +} + +bool hyn_touch_init_with_config(const HynTouchConfig *config) +{ + if (config == nullptr) { + return false; + } + + return initialize_touch_core(*config); +} + +bool hyn_touch_is_ready() +{ + return g_touch.ready; +} + +uint8_t hyn_touch_get_point(int16_t *x_array, int16_t *y_array, uint8_t get_point) +{ + if (!g_touch.ready || !g_touch.data || x_array == nullptr || y_array == nullptr || get_point == 0) { + return 0; + } + + if (!g_touch.press_flag) { + return 0; + } + g_touch.press_flag = false; + + uint8_t point_count = 0; + g_touch.data->hyn_irq_flg = 1; + if (g_touch.data->work_mode < DIFF_MODE) { + const int ret = g_touch.data->hyn_fuc_used->tp_report(); + point_count = (g_touch.data->rp_buf.report_need & REPORT_POS) ? g_touch.data->rp_buf.rep_num : 0; + apply_axis_transform(point_count); + + for (uint8_t i = 0; i < point_count && i < get_point; ++i) { + x_array[i] = g_touch.data->rp_buf.pos_info[i].pos_x; + y_array[i] = g_touch.data->rp_buf.pos_info[i].pos_y; + } + +#if HYN_TOUCH_RUNTIME_LOG + printf("ret:%d num:%d xy:", ret, point_count); + for (uint8_t i = 0; i < point_count; ++i) { + printf("(%d,%d) ", g_touch.data->rp_buf.pos_info[i].pos_x, g_touch.data->rp_buf.pos_info[i].pos_y); + } + printf("key_id:%d, key_st:%d\n", g_touch.data->rp_buf.key_id, g_touch.data->rp_buf.key_state); +#else + (void)ret; +#endif + } + + handle_key_report(); + g_touch.data->rp_buf.report_need = REPORT_NONE; + return point_count; +} + +bool hyn_touch_get_key_state(uint8_t key_id) +{ + if (key_id >= 3) { + return false; + } + return g_touch.key_pressed[key_id]; +} + +bool hyn_touch_get_key_seen(uint8_t key_id) +{ + if (key_id >= 3) { + return false; + } + return g_touch.key_seen[key_id]; +} + +void hyn_touch_clear_key_seen() +{ + memset(g_touch.key_seen, 0, sizeof(g_touch.key_seen)); +} + +void hyn_touch_set_key_callback(HynTouchKeyCallback callback, void *user_data) +{ + g_touch.key_callback = callback; + g_touch.key_callback_user_data = user_data; +} + +void hyn_touch_before_light_sleep() +{ + detach_irq_handler(); + g_touch.press_flag = false; +} + +void hyn_touch_after_light_sleep() +{ + g_touch.press_flag = false; + hyn_touch_resume(); + g_touch.press_flag = false; + attach_irq_handler(); +} + +void hyn_touch_resume() +{ + if (!g_touch.ready || !g_touch.data || !g_touch.data->hyn_fuc_used || !g_touch.data->hyn_fuc_used->tp_resum) { + return; + } + + g_touch.data->hyn_fuc_used->tp_resum(); +} + +void hyn_sleep() +{ + if (!g_touch.ready || !g_touch.data || !g_touch.data->hyn_fuc_used || !g_touch.data->hyn_fuc_used->tp_supend) { + return; + } + +#if HYN_TOUCH_RUNTIME_LOG + printf("hyn_sleep = %p\n", g_touch.data->hyn_fuc_used->tp_supend); +#endif + g_touch.data->hyn_fuc_used->tp_supend(); + delay(100); +} diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/HynTouch.h b/variants/esp32s3/t-deck-max/HynTouch/src/HynTouch.h new file mode 100644 index 00000000000..b946391dc85 --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/HynTouch.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +class ExtensionIOXL9555; + +typedef void (*HynTouchKeyCallback)(uint8_t key_id, bool pressed, void *user_data); +typedef bool (*HynTouchVirtualGpioWriteCallback)(uint32_t gpio_id, bool value, void *user_data); +typedef bool (*HynTouchVirtualGpioReadCallback)(uint32_t gpio_id, int *value, void *user_data); + +struct HynTouchConfig { + int sda_pin; + int scl_pin; + int reset_pin; + int irq_pin; + uint8_t max_touch_points; + uint16_t x_resolution; + uint16_t y_resolution; + bool swap_xy; + bool reverse_x; + bool reverse_y; +}; + +HynTouchConfig hyn_touch_default_config(); +bool hyn_touch_init(); +bool hyn_touch_init_with_config(const HynTouchConfig *config); +bool hyn_touch_is_ready(); +uint8_t hyn_touch_get_point(int16_t *x_array, int16_t *y_array, uint8_t get_point); +bool hyn_touch_get_key_state(uint8_t key_id); +bool hyn_touch_get_key_seen(uint8_t key_id); +void hyn_touch_clear_key_seen(); +void hyn_touch_set_key_callback(HynTouchKeyCallback callback, void *user_data); +void hyn_touch_set_virtual_gpio_callbacks(HynTouchVirtualGpioWriteCallback write_callback, + HynTouchVirtualGpioReadCallback read_callback, void *user_data); +void hyn_touch_attach_xl9555(ExtensionIOXL9555 *io); +void hyn_touch_before_light_sleep(); +void hyn_touch_after_light_sleep(); +void hyn_touch_resume(); +void hyn_sleep(); diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cfg.h b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cfg.h new file mode 100644 index 00000000000..0fd5ab50988 --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cfg.h @@ -0,0 +1,23 @@ + +#ifndef _HYNITRON_CFG_H +#define _HYNITRON_CFG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/**delay ms */ +#define mdelay(ms) hyn_delay_ms(ms) +#define msleep(ms) hyn_delay_ms(ms) + +/**function config */ +#define HYN_POWER_ON_UPDATA (0) // touch fw updata + +#define MAX_POINTS_REPORT (5) // max report point + +#define HYN_DRIVER_VERSION "== Hynitron V2.10 20241111 ==" + +#ifdef __cplusplus +} +#endif +#endif diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/hyn_core.h b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_core.h new file mode 100644 index 00000000000..83dd27b28c8 --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_core.h @@ -0,0 +1,239 @@ +#ifndef HYNITRON_CORE_H +#define HYNITRON_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "hyn_cfg.h" +#include +#include +#include +#include + +#include "driver/gpio.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" + +#define HYN_INFO(fmt, args...) // printf("[HYN]"fmt"\n", ##args) +#define HYN_INFO2(fmt, args...) // if(hyn_data->log_level > 0)printf("[HYN]"fmt"\n", ##args) +#define HYN_INFO3(fmt, args...) // if(hyn_data->log_level > 1)printf("[HYN]"fmt"\n", ##args) +#define HYN_INFO4(fmt, args...) // if(hyn_data->log_level > 2)printf("[HYN]"fmt"\n", ##args) +#define HYN_ERROR(fmt, args...) // printf("[HYN][Error]%s:"fmt"\n",__func__,##args) +#define HYN_ENTER() // printf("[HYN][enter]%s\n",__func__) + +// #define IS_ERR_OR_NULL(x) (x <= 0) +#define U8TO16(x1, x2) ((((x1)&0xFF) << 8) | ((x2)&0xFF)) +#define U8TO32(x1, x2, x3, x4) ((((x1)&0xFF) << 24) | (((x2)&0xFF) << 16) | (((x3)&0xFF) << 8) | ((x4)&0xFF)) +#define U16REV(x) ((((x) << 8) & 0xFF00) | (((x) >> 8) & 0x00FF)) + +// #undef bool +// #undef NULL +#undef FALSE +#undef TRUE +#undef DISABLE +#undef ENABLE +// #define NULL ((void*)0) +#define FALSE (-1) +#define TRUE (0) +#define DISABLE (0) +#define ENABLE (1) + +#ifndef HIGH +#define HIGH (1) +#endif + +#define PS_FAR_AWAY 1 +#define PS_NEAR 0 + +#define MULTI_OPEN_TEST (0x80) +#define MULTI_SHORT_TEST (0x01) +#define MULTI_SCAP_TEST (0x02) + +typedef uint8_t u8; +// typedef uint8_t bool; +typedef int8_t s8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef int32_t s32; + +enum work_mode { + NOMAL_MODE = 0, + GESTURE_MODE = 1, + LP_MODE = 2, + DEEPSLEEP = 3, + DIFF_MODE = 4, + RAWDATA_MODE = 5, + BASELINE_MODE = 6, + CALIBRATE_MODE = 7, + FAC_TEST_MODE = 8, + ENTER_BOOT_MODE = 0xCA, +}; + +enum report_typ { REPORT_NONE = 0, REPORT_POS = 0x01, REPORT_KEY = 0x02, REPORT_GES = 0x04, REPORT_PROX = 0x08 }; + +enum fac_test_ero { + FAC_TEST_PASS = 0, + FAC_GET_DATA_FAIL = -1, + FAC_GET_CFG_FAIL = -4, + FAC_TEST_OPENL_FAIL = -5, + FAC_TEST_OPENH_FAIL = -7, + FAC_TEST_SHORT_FAIL = -6, + FAC_TEST_SCAP_FAIL = -8, +}; + +enum ges_idx { + IDX_U = 0, + IDX_UP, + IDX_DOWN, + IDX_LEFT, + IDX_RIGHT, + IDX_O, + IDX_e, + IDX_M, + IDX_L, + IDX_W, + IDX_S, + IDX_V, + IDX_C, + IDX_Z, + IDX_POWER, + IDX_NULL = 0xFF, +}; + +struct hyn_plat_data { + int reset_gpio; + int irq_gpio; + + u32 x_resolution; + u32 y_resolution; + int swap_xy; + int reverse_x; + int reverse_y; + + int max_touch_num; + int key_num; + u32 key_x_coords[8]; // max support 8 keys + u32 key_y_coords; + u32 key_code[8]; +}; + +struct hyn_chip_series { + u32 part_no; + u32 moudle_id; + u8 chip_name[20]; + u8 *fw_bin; +}; + +struct ts_frame { + u8 rep_num; + enum report_typ report_need; + u8 key_id; + u8 key_state; + struct { + u8 pos_id; + u8 event; + u16 pos_x; + u16 pos_y; + u16 pres_z; + } pos_info[MAX_POINTS_REPORT]; +}; + +struct tp_info { + u8 fw_sensor_txnum; + u8 fw_sensor_rxnum; + u8 fw_key_num; + u8 reserve; + u16 fw_res_y; + u16 fw_res_x; + u32 fw_boot_time; + u32 fw_project_id; + u32 fw_chip_type; + u32 fw_ver; + u32 ic_fw_checksum; + u32 fw_module_id; +}; + +struct hyn_ts_data { + u16 bus_type; + u8 salve_addr; + int gpio_irq; + int esd_fail_cnt; + u32 esd_last_value; + enum work_mode work_mode; + + int power_is_on; + u8 hyn_irq_flg; + struct hyn_plat_data plat_data; + struct tp_info hw_info; + struct ts_frame rp_buf; + + int boot_is_pass; + int need_updata_fw; + u8 fw_file_name[128]; + u8 *fw_updata_addr; + int fw_updata_len; + int fw_dump_state; + u8 fw_updata_process; + u8 host_cmd_save[16]; + + u8 log_level; + u8 prox_is_enable; + u8 prox_state; + + u8 gesture_is_enable; + u8 gesture_id; + const struct hyn_ts_fuc *hyn_fuc_used; +}; + +struct hyn_ts_fuc { + void (*tp_rest)(void); + int (*tp_report)(void); + int (*tp_supend)(void); + int (*tp_resum)(void); + int (*tp_chip_init)(struct hyn_ts_data *ts_data); + int (*tp_updata_fw)(u8 *bin_addr, u16 len); + int (*tp_set_workmode)(enum work_mode mode, u8 enable); + u32 (*tp_check_esd)(void); + int (*tp_prox_handle)(u8 cmd); + int (*tp_get_dbg_data)(u8 *buf, u16 len); + int (*tp_get_test_result)(u8 *buf, u16 len); +}; + +// hyn_i2c.c +esp_err_t hyn_i2c_init(u8 pin_sda, u8 pin_scl); +int hyn_write_data(struct hyn_ts_data *ts_data, u8 *buf, u8 reg_len, u16 len); +int hyn_read_data(struct hyn_ts_data *ts_data, u8 *buf, u16 len); +int hyn_wr_reg(struct hyn_ts_data *ts_data, u32 reg_addr, u8 reg_len, u8 *rbuf, u16 rlen); +int gpio_set_value(uint32_t gpio_id, bool vlue); +bool gpio_get_value(uint32_t gpio_id); +void hyn_delay_ms(int cnt); + +// hyn_ts_ext.c +void hyn_irq_set(struct hyn_ts_data *ts_data, u8 value); +void hyn_set_i2c_addr(struct hyn_ts_data *ts_data, u8 addr); +u16 hyn_sum16(int val, u8 *buf, u16 len); +u32 hyn_sum32(int val, u32 *buf, u16 len); +void hyn_esdcheck_switch(struct hyn_ts_data *ts_data, u8 enable); +int copy_for_updata(struct hyn_ts_data *ts_data, u8 *buf, u32 offset, u16 len); +int hyn_wait_irq_timeout(struct hyn_ts_data *ts_data, int msec); +int factory_multitest(struct hyn_ts_data *ts_data, char *cfg_path, u8 *data, s16 *test_th, u8 test_item); +int fac_test_log_save(char *log_name, struct hyn_ts_data *ts_data, s16 *test_data, int test_ret, u8 test_item); + +// ic type +extern const struct hyn_ts_fuc cst1xx_fuc; +extern const struct hyn_ts_fuc cst3xx_fuc; +extern const struct hyn_ts_fuc cst66xx_fuc; +extern const struct hyn_ts_fuc cst7xx_fuc; +extern const struct hyn_ts_fuc cst8xxT_fuc; +extern const struct hyn_ts_fuc cst92xx_fuc; +extern const struct hyn_ts_fuc cst3240_fuc; +extern const struct hyn_ts_fuc cst226se_fuc; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst226se.c b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst226se.c new file mode 100644 index 00000000000..26e505cc792 --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst226se.c @@ -0,0 +1,248 @@ +#include "hyn_core.h" + +#define BOOT_I2C_ADDR (0x5A) +// #define MAIN_I2C_ADDR (0x5A) +#define MAIN_I2C_ADDR (0x1A) + +#define RW_REG_LEN (2) + +#define CST226SE_BIN_SIZE (7 * 1024 + 512) + +static const char *TAG = "hyn_cst226se"; +static struct hyn_ts_data *hyn_226data = NULL; +static const u8 gest_map_tbl[33] = {0xff, 4, 1, 3, 2, 5, 12, 6, 7, 7, 9, 11, 10, 13, 12, 7, 7, + 6, 10, 6, 5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 14}; + +static int cst226se_updata_tpinfo(void); +static int cst226se_set_workmode(enum work_mode mode, u8 enable); +static void cst226se_rst(void); + +static int cst226se_init(struct hyn_ts_data *ts_data) +{ + int ret = 0; + hyn_226data = ts_data; + cst226se_rst(); + mdelay(50); + hyn_set_i2c_addr(hyn_226data, MAIN_I2C_ADDR); + ret = cst226se_updata_tpinfo(); + ret |= cst226se_set_workmode(NOMAL_MODE, 0); + return ret; +} + +static int cst226se_report(void) +{ + u8 buf[80] = {0}; + u8 finger_num = 0, key_flg = 0, tmp_dat; + int len = 0; + struct hyn_plat_data *dt = &hyn_226data->plat_data; + int ret = 0, retry = 2; + switch (hyn_226data->work_mode) { + case NOMAL_MODE: + retry = 2; + while (retry--) { + ret = hyn_wr_reg(hyn_226data, 0xD000, 2, buf, 7); + if (ret || buf[6] != 0xAB || buf[0] == 0xAB) { + ret = -2; + continue; + } + finger_num = buf[5] & 0x7F; + if (finger_num > dt->max_touch_num) { + finger_num = dt->max_touch_num; + } + key_flg = (buf[5] & 0x80) ? 1 : 0; + len = 0; + if (finger_num > 1) { + len += (finger_num - 1) * 5; + } + if (key_flg && finger_num) { + len += 3; + } + if (len > 0) { + ret = hyn_wr_reg(hyn_226data, 0xD007, 2, &buf[5], len); + } + ret |= hyn_wr_reg(hyn_226data, 0xD000AB, 3, buf, 0); + if (ret) { + ret = -3; + continue; + } + ret = 0; + break; + } + if (ret) { + hyn_wr_reg(hyn_226data, 0xD000AB, 3, buf, 0); + ESP_LOGE(TAG, "read frame failed"); + break; + } + if (key_flg) { // key + if (hyn_226data->rp_buf.report_need == REPORT_NONE) { + hyn_226data->rp_buf.report_need |= REPORT_KEY; + } + len = finger_num ? (len + 5) : 3; + hyn_226data->rp_buf.key_id = (buf[len - 2] >> 4) - 1; + hyn_226data->rp_buf.key_state = (buf[len - 3] & 0x0F) == 0x03 ? 1 : 0; + ESP_LOGI(TAG, "key_id:%x state:%x", hyn_226data->rp_buf.key_id, hyn_226data->rp_buf.key_state); + } + if (finger_num) { + u16 index = 0, i = 0; + u8 touch_down = 0; + if (hyn_226data->rp_buf.report_need == REPORT_NONE) { + hyn_226data->rp_buf.report_need |= REPORT_POS; + } + hyn_226data->rp_buf.rep_num = finger_num; + for (i = 0; i < finger_num; i++) { + index = i * 5; + hyn_226data->rp_buf.pos_info[i].pos_id = (buf[index] >> 4) & 0x0F; + hyn_226data->rp_buf.pos_info[i].event = (buf[index] & 0x0F) == 0x06 ? 1 : 0; + hyn_226data->rp_buf.pos_info[i].pos_x = ((u16)buf[index + 1] << 4) + ((buf[index + 3] >> 4) & 0x0F); + hyn_226data->rp_buf.pos_info[i].pos_y = ((u16)buf[index + 2] << 4) + (buf[index + 3] & 0x0F); + hyn_226data->rp_buf.pos_info[i].pres_z = buf[index + 4]; + if (hyn_226data->rp_buf.pos_info[i].event) + touch_down++; + // ESP_LOGI(TAG, "report_id = %d, xy = + // %d,%d",hyn_226data->rp_buf.pos_info[i].pos_id,hyn_226data->rp_buf.pos_info[i].pos_x,hyn_226data->rp_buf.pos_info[i].pos_y); + } + if (0 == touch_down) { + hyn_226data->rp_buf.rep_num = 0; + } + } + break; + case GESTURE_MODE: + ret = hyn_wr_reg(hyn_226data, 0xD04C, 2, &tmp_dat, 1); + if ((tmp_dat & 0x7F) <= 32) { + tmp_dat = tmp_dat & 0x7F; + hyn_226data->gesture_id = gest_map_tbl[tmp_dat]; + hyn_226data->rp_buf.report_need |= REPORT_GES; + } + break; + default: + break; + } + return 0; +} + +static int cst226se_set_workmode(enum work_mode mode, u8 enable) +{ + int ret = 0; + ESP_LOGI(TAG, "enter %s", __func__); + hyn_226data->work_mode = mode; + if (mode != NOMAL_MODE) + hyn_esdcheck_switch(hyn_226data, DISABLE); + switch (mode) { + case NOMAL_MODE: + hyn_irq_set(hyn_226data, ENABLE); + hyn_esdcheck_switch(hyn_226data, enable); + hyn_wr_reg(hyn_226data, 0xD10B, 2, NULL, 0); // soft rst + hyn_wr_reg(hyn_226data, 0xD109, 2, NULL, 0); + break; + case GESTURE_MODE: + hyn_wr_reg(hyn_226data, 0xD04C80, 3, NULL, 0); + break; + case LP_MODE: + break; + case DIFF_MODE: + hyn_wr_reg(hyn_226data, 0xD10B, 2, NULL, 0); + hyn_wr_reg(hyn_226data, 0xD10D, 2, NULL, 0); + break; + case RAWDATA_MODE: + hyn_wr_reg(hyn_226data, 0xD10B, 2, NULL, 0); + hyn_wr_reg(hyn_226data, 0xD10A, 2, NULL, 0); + break; + case FAC_TEST_MODE: + hyn_wr_reg(hyn_226data, 0xD10B, 2, NULL, 0); + hyn_wr_reg(hyn_226data, 0xD119, 2, NULL, 0); + msleep(50); // wait switch to fac mode + break; + case DEEPSLEEP: + hyn_irq_set(hyn_226data, DISABLE); + hyn_wr_reg(hyn_226data, 0xD105, 2, NULL, 0); + break; + default: + hyn_esdcheck_switch(hyn_226data, ENABLE); + hyn_226data->work_mode = NOMAL_MODE; + break; + } + return ret; +} + +static int cst226se_supend(void) +{ + ESP_LOGI(TAG, "touch sleep"); + + // cst226se_set_workmode(DEEPSLEEP,0); + + hyn_irq_set(hyn_226data, DISABLE); + hyn_wr_reg(hyn_226data, 0xD105, 2, NULL, 0); + return 0; +} + +static int cst226se_resum(void) +{ + ESP_LOGI(TAG, "enter %s", __func__); + cst226se_rst(); + msleep(50); + cst226se_set_workmode(NOMAL_MODE, 0); + return 0; +} + +static void cst226se_rst(void) +{ + if (hyn_226data->work_mode == ENTER_BOOT_MODE) { + hyn_set_i2c_addr(hyn_226data, MAIN_I2C_ADDR); + } + gpio_set_value(hyn_226data->plat_data.reset_gpio, 0); + msleep(10); + gpio_set_value(hyn_226data->plat_data.reset_gpio, 1); +} + +static int cst226se_updata_tpinfo(void) +{ + u8 buf[28]; + struct tp_info *ic = &hyn_226data->hw_info; + int ret = 0, retry = 5; + while (--retry) { + ret = hyn_wr_reg(hyn_226data, 0xD101, 2, buf, 0); + mdelay(1); + ret |= hyn_wr_reg(hyn_226data, 0xD1F4, 2, buf, 28); + cst226se_set_workmode(NOMAL_MODE, 0); + if (ret == 0 && U8TO16(buf[19], buf[18]) == 0x00a8) { + break; + } + msleep(1); + } + + if (ret || retry == 0) { + ESP_LOGE(TAG, "cst226se_updata_tpinfo failed"); + + for (int i = 0; i < 28; i++) { + printf("%x ", buf[i]); + } + return -1; + } + + ic->fw_sensor_txnum = buf[0]; + ic->fw_sensor_rxnum = buf[2]; + ic->fw_key_num = buf[3]; + ic->fw_res_y = (buf[7] << 8) | buf[6]; + ic->fw_res_x = (buf[5] << 8) | buf[4]; + ic->fw_project_id = (buf[17] << 8) | buf[16]; + ic->fw_chip_type = U8TO16(buf[19], buf[18]); + ic->fw_ver = (buf[23] << 24) | (buf[22] << 16) | (buf[21] << 8) | buf[20]; + + ESP_LOGI(TAG, "IC_info fw_project_id:%04lx ictype:%04lx fw_ver:%lx checksum:%#lx", ic->fw_project_id, ic->fw_chip_type, + ic->fw_ver, ic->ic_fw_checksum); + return 0; +} + +const struct hyn_ts_fuc cst226se_fuc = { + .tp_rest = cst226se_rst, + .tp_report = cst226se_report, + .tp_supend = cst226se_supend, + .tp_resum = cst226se_resum, + .tp_chip_init = cst226se_init, + .tp_updata_fw = NULL, + .tp_set_workmode = cst226se_set_workmode, + .tp_check_esd = NULL, + .tp_prox_handle = NULL, + .tp_get_dbg_data = NULL, + .tp_get_test_result = NULL, +}; diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst3xx.c b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst3xx.c new file mode 100644 index 00000000000..65c89000bca --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst3xx.c @@ -0,0 +1,219 @@ +#include "hyn_core.h" + +#define BOOT_I2C_ADDR (0x1A) +#define MAIN_I2C_ADDR (0x1A) +#define RW_REG_LEN (2) +#define CST3xx_BIN_SIZE (24 * 1024 + 24) +#define SOFT_RST_ENABLE (0) + +static const char *TAG = "hyn_cst3xx"; +static struct hyn_ts_data *hyn_3xxdata = NULL; +static const u8 gest_map_tbl[33] = {0xff, 4, 1, 3, 2, 5, 12, 6, 7, 7, 9, 11, 10, 13, 12, 7, 7, + 6, 10, 6, 5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 14}; + +static int cst3xx_updata_tpinfo(void); +static int cst3xx_set_workmode(enum work_mode mode, u8 enable); +static void cst3xx_rst(void); + +static int cst3xx_init(struct hyn_ts_data *ts_data) +{ + int ret = 0; + hyn_3xxdata = ts_data; + hyn_set_i2c_addr(hyn_3xxdata, MAIN_I2C_ADDR); + cst3xx_rst(); + mdelay(50); + ret = cst3xx_updata_tpinfo(); + ret |= cst3xx_set_workmode(NOMAL_MODE, 0); + return ret; +} + +static int cst3xx_report(void) +{ + u8 buf[80]; + u8 finger_num = 0, len = 0, write_tail_end = 0, key_state = 0, key_id = 0, tmp_dat; + struct hyn_plat_data *dt = &hyn_3xxdata->plat_data; + int ret = 0; + hyn_3xxdata->rp_buf.report_need = REPORT_NONE; + switch (hyn_3xxdata->work_mode) { + case NOMAL_MODE: + write_tail_end = 1; + ret = hyn_wr_reg(hyn_3xxdata, 0xD000, 2, buf, 7); + if (ret || buf[6] != 0xAB || buf[0] == 0xAB) { + break; + } + len = buf[5] & 0x7F; + if (len > dt->max_touch_num) { + len = dt->max_touch_num; + } + if (buf[5] & 0x80) { // key + if (buf[5] == 0x80) { + key_id = (buf[1] >> 4) - 1; + key_state = buf[0]; + } else { + finger_num = len; + len = (len - 1) * 5 + 3; + ret = hyn_wr_reg(hyn_3xxdata, 0xD007, 2, &buf[5], len); + len += 5; + key_id = (buf[len - 2] >> 4) - 1; + key_state = buf[len - 3]; + } + if (key_state & 0x80) { + hyn_3xxdata->rp_buf.report_need |= REPORT_KEY; + if ((key_id == hyn_3xxdata->rp_buf.key_id || 0 == hyn_3xxdata->rp_buf.key_state) && key_state == 0x83) { + hyn_3xxdata->rp_buf.key_id = key_id; + hyn_3xxdata->rp_buf.key_state = 1; + } else { + hyn_3xxdata->rp_buf.key_state = 0; + } + } + } else { // pos + u16 index = 0; + u8 i = 0; + finger_num = len; + hyn_3xxdata->rp_buf.report_need |= REPORT_POS; + if (finger_num > 1) { + len = (len - 1) * 5 + 1; + ret = hyn_wr_reg(hyn_3xxdata, 0xD007, 2, &buf[5], len); + } + hyn_3xxdata->rp_buf.rep_num = finger_num; + for (i = 0; i < finger_num; i++) { + index = i * 5; + hyn_3xxdata->rp_buf.pos_info[i].pos_id = (buf[index] >> 4) & 0x0F; + hyn_3xxdata->rp_buf.pos_info[i].event = (buf[index] & 0x0F) == 0x06 ? 1 : 0; + hyn_3xxdata->rp_buf.pos_info[i].pos_x = ((u16)buf[index + 1] << 4) + ((buf[index + 3] >> 4) & 0x0F); + hyn_3xxdata->rp_buf.pos_info[i].pos_y = ((u16)buf[index + 2] << 4) + (buf[index + 3] & 0x0F); + hyn_3xxdata->rp_buf.pos_info[i].pres_z = buf[index + 4]; + // HYN_INFO("report_id = %d, xy = + // %d,%d",hyn_3xxdata->rp_buf.pos_info[i].pos_id,hyn_3xxdata->rp_buf.pos_info[i].pos_x,hyn_3xxdata->rp_buf.pos_info[i].pos_y); + } + } + break; + case GESTURE_MODE: + ret = hyn_wr_reg(hyn_3xxdata, 0xD04C, 2, &tmp_dat, 1); + if ((tmp_dat & 0x7F) <= 32) { + tmp_dat = tmp_dat & 0x7F; + hyn_3xxdata->gesture_id = gest_map_tbl[tmp_dat]; + hyn_3xxdata->rp_buf.report_need |= REPORT_GES; + } + break; + default: + break; + } + if (write_tail_end) { + hyn_wr_reg(hyn_3xxdata, 0xD000AB, 3, buf, 0); + } + return 0; +} + +static int cst3xx_set_workmode(enum work_mode mode, u8 enable) +{ + int ret = 0; + hyn_3xxdata->work_mode = mode; + if (mode != NOMAL_MODE) + hyn_esdcheck_switch(hyn_3xxdata, DISABLE); + switch (mode) { + case NOMAL_MODE: + hyn_irq_set(hyn_3xxdata, ENABLE); + hyn_esdcheck_switch(hyn_3xxdata, enable); + // hyn_wr_reg(hyn_3xxdata,0xD100,2,NULL,0); + hyn_wr_reg(hyn_3xxdata, 0xD109, 2, NULL, 0); + break; + case GESTURE_MODE: + hyn_wr_reg(hyn_3xxdata, 0xD04C80, 3, NULL, 0); + break; + case LP_MODE: + break; + case DIFF_MODE: + hyn_wr_reg(hyn_3xxdata, 0xD10D, 2, NULL, 0); + break; + case RAWDATA_MODE: + hyn_wr_reg(hyn_3xxdata, 0xD10A, 2, NULL, 0); + break; + case FAC_TEST_MODE: + hyn_wr_reg(hyn_3xxdata, 0xD119, 2, NULL, 0); + break; + case DEEPSLEEP: + hyn_irq_set(hyn_3xxdata, DISABLE); + hyn_wr_reg(hyn_3xxdata, 0xD105, 2, NULL, 0); + break; + default: + hyn_esdcheck_switch(hyn_3xxdata, ENABLE); + hyn_3xxdata->work_mode = NOMAL_MODE; + break; + } + return ret; +} + +static int cst3xx_supend(void) +{ + HYN_ENTER(); + // cst3xx_set_workmode(DEEPSLEEP,0); + ESP_LOGI(TAG, "touch sleep"); + + hyn_irq_set(hyn_3xxdata, DISABLE); + hyn_wr_reg(hyn_3xxdata, 0xD105, 2, NULL, 0); + return 0; +} + +static int cst3xx_resum(void) +{ + HYN_ENTER(); + cst3xx_rst(); + msleep(50); + cst3xx_set_workmode(NOMAL_MODE, 0); + return 0; +} + +static void cst3xx_rst(void) +{ + if (hyn_3xxdata->work_mode == ENTER_BOOT_MODE) { + hyn_set_i2c_addr(hyn_3xxdata, MAIN_I2C_ADDR); + } +#if SOFT_RST_ENABLE + hyn_wr_reg(hyn_3xxdata, 0xD10E, 2, NULL, 0); +#endif + gpio_set_value(hyn_3xxdata->plat_data.reset_gpio, 0); + msleep(10); + gpio_set_value(hyn_3xxdata->plat_data.reset_gpio, 1); +} + +static int cst3xx_updata_tpinfo(void) +{ + u8 buf[28]; + struct tp_info *ic = &hyn_3xxdata->hw_info; + int ret = 0; + ret = hyn_wr_reg(hyn_3xxdata, 0xD101, 2, buf, 0); + mdelay(1); + ret |= hyn_wr_reg(hyn_3xxdata, 0xD1F4, 2, buf, 28); + if (ret) { + ESP_LOGE(TAG, "cst3xx_updata_tpinfo failed"); + return -1; + } + + ic->fw_sensor_txnum = buf[0]; + ic->fw_sensor_rxnum = buf[2]; + ic->fw_key_num = buf[3]; + ic->fw_res_y = (buf[7] << 8) | buf[6]; + ic->fw_res_x = (buf[5] << 8) | buf[4]; + ic->fw_project_id = (buf[17] << 8) | buf[16]; + ic->fw_chip_type = (buf[19] << 8) | buf[18]; + ic->fw_ver = (buf[23] << 24) | (buf[22] << 16) | (buf[21] << 8) | buf[20]; + + ESP_LOGI(TAG, "IC_info fw_project_id:%04lx ictype:%04lx fw_ver:%lx checksum:%#lx", ic->fw_project_id, ic->fw_chip_type, + ic->fw_ver, ic->ic_fw_checksum); + return 0; +} + +const struct hyn_ts_fuc cst3xx_fuc = { + .tp_rest = cst3xx_rst, + .tp_report = cst3xx_report, + .tp_supend = cst3xx_supend, + .tp_resum = cst3xx_resum, + .tp_chip_init = cst3xx_init, + .tp_updata_fw = NULL, + .tp_set_workmode = cst3xx_set_workmode, + .tp_check_esd = NULL, + .tp_prox_handle = NULL, + .tp_get_dbg_data = NULL, + .tp_get_test_result = NULL, +}; diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst66xx.c b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst66xx.c new file mode 100644 index 00000000000..7c6728ad4fb --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_cst66xx.c @@ -0,0 +1,274 @@ +#include "hyn_core.h" + +#define BOOT_I2C_ADDR (0x5A) +// #define MAIN_I2C_ADDR (0x58) //use 2 slave addr +#define MAIN_I2C_ADDR (0x1A) // use 2 slave addr + +#define PART_NO_EN (0) +#define cst66xx_BIN_SIZE (40 * 1024) //(36*1024) +#define MODULE_ID_ADDR (0xA400) +#define PARTNUM_ADDR (0xFF10) + +static const char *TAG = "hyn_cst66xx"; +static struct hyn_ts_data *hyn_66xxdata = NULL; +static const u8 gest_map_tbl[] = { + IDX_POWER, // GESTURE_LABEL_CLICK, + IDX_POWER, // GESTURE_LABEL_CLICK2, + IDX_UP, // GESTURE_LABEL_TOP, + IDX_DOWN, // GESTURE_LABEL_BOTTOM, + IDX_LEFT, // GESTURE_LABEL_LEFT, + IDX_RIGHT, // GESTURE_LABEL_RIGHT, + IDX_C, // GESTURE_LABEL_C, + IDX_e, // GESTURE_LABEL_E, + IDX_V, // GESTURE_LABEL_v, + IDX_NULL, // GESTURE_LABEL_^, + IDX_NULL, // GESTURE_LABEL_>, + IDX_NULL, // GESTURE_LABEL_<, + IDX_M, // GESTURE_LABEL_M, + IDX_W, // GESTURE_LABEL_W, + IDX_O, // GESTURE_LABEL_O, + IDX_S, // GESTURE_LABEL_S, + IDX_Z, // GESTURE_LABEL_Z +}; + +static void cst66xx_rst(void); +static int cst66xx_set_workmode(enum work_mode mode, u8 enable); +static int cst66xx_updata_tpinfo(void); + +static int cst66xx_init(struct hyn_ts_data *ts_data) +{ + int ret = 0; + HYN_ENTER(); + hyn_66xxdata = ts_data; + hyn_set_i2c_addr(hyn_66xxdata, MAIN_I2C_ADDR); + cst66xx_rst(); // exit boot + mdelay(50); + ret = cst66xx_updata_tpinfo(); + if (ret) + ESP_LOGE(TAG, "get tpinfo failed"); + ret |= cst66xx_set_workmode(NOMAL_MODE, 0); + return ret; +} + +static int cst66xx_report(void) +{ + u8 buf[80], i = 0; + u8 finger_num = 0, key_num = 0, report_typ = 0, key_state = 0, key_id = 0, tmp_dat = 0; + int ret = 0, retry = 2; + + while (retry--) { // read point + ret = hyn_wr_reg(hyn_66xxdata, 0xD0070000, 0x80 | 4, buf, 9); + report_typ = buf[2]; // FF:pos F0:ges E0:prox + finger_num = buf[3] & 0x0f; + key_num = ((buf[3] & 0xf0) >> 4); + if (finger_num + key_num <= MAX_POINTS_REPORT) { + if (key_num + finger_num > 1) { + ret |= hyn_read_data(hyn_66xxdata, &buf[9], (key_num + finger_num - 1) * 5); + } + if (hyn_sum16(0x55, &buf[4], (key_num + finger_num) * 5) != (buf[0] | buf[1] << 8)) { + ret = -1; + } + } else { + ret = -2; + } + if (ret && retry) + continue; + ret |= hyn_wr_reg(hyn_66xxdata, 0xD00002AB, 4, buf, 0); + if (ret == 0) { + break; + } + } + if (ret) + return ret; + + if ((report_typ == 0xff) && ((finger_num + key_num) > 0)) { + if (key_num) { + key_id = buf[8] & 0x0f; + key_state = buf[8] >> 4; + if (hyn_66xxdata->rp_buf.report_need == REPORT_NONE) { // Mutually exclusive reporting of coordinates and keys + hyn_66xxdata->rp_buf.report_need |= REPORT_KEY; + } + hyn_66xxdata->rp_buf.key_id = key_id; + hyn_66xxdata->rp_buf.key_state = key_state != 0 ? 1 : 0; + } + + if (finger_num) { // pos + u16 index = 0; + u8 touch_down = 0; + if (hyn_66xxdata->rp_buf.report_need == REPORT_NONE) { // Mutually exclusive reporting of coordinates and keys + hyn_66xxdata->rp_buf.report_need |= REPORT_POS; + } + hyn_66xxdata->rp_buf.rep_num = finger_num; + for (i = 0; i < finger_num; i++) { + index = (key_num + i) * 5; + hyn_66xxdata->rp_buf.pos_info[i].pos_id = buf[index + 8] & 0x0F; + hyn_66xxdata->rp_buf.pos_info[i].event = buf[index + 8] >> 4; + hyn_66xxdata->rp_buf.pos_info[i].pos_x = buf[index + 4] + ((u16)(buf[index + 7] & 0x0F) << 8); // x is rx + // direction + hyn_66xxdata->rp_buf.pos_info[i].pos_y = buf[index + 5] + ((u16)(buf[index + 7] & 0xf0) << 4); + hyn_66xxdata->rp_buf.pos_info[i].pres_z = buf[index + 6]; + if (hyn_66xxdata->rp_buf.pos_info[i].event) { + touch_down++; + } + } + if (0 == touch_down) { + hyn_66xxdata->rp_buf.rep_num = 0; + } + } + } else if (report_typ == 0xF0) { + tmp_dat = buf[8] & 0xff; + if ((tmp_dat & 0x7F) < sizeof(gest_map_tbl) && gest_map_tbl[tmp_dat] != IDX_NULL) { + hyn_66xxdata->gesture_id = gest_map_tbl[tmp_dat]; + hyn_66xxdata->rp_buf.report_need |= REPORT_GES; + ESP_LOGI(TAG, "gesture_id:%d", tmp_dat); + } + } else if (report_typ == 0xE0) { // proximity + u8 state = buf[4] ? PS_FAR_AWAY : PS_NEAR; + if (hyn_66xxdata->prox_is_enable && hyn_66xxdata->prox_state != state) { + hyn_66xxdata->prox_state = state; + hyn_66xxdata->rp_buf.report_need |= REPORT_PROX; + } + } + return ret; +} + +static int cst66xx_set_workmode(enum work_mode mode, u8 enable) +{ + int ret = 0; + ESP_LOGI(TAG, "set_workmode:%d", mode); + hyn_66xxdata->work_mode = mode; + if (mode != NOMAL_MODE) { + hyn_esdcheck_switch(hyn_66xxdata, DISABLE); + } + if (FAC_TEST_MODE == mode) { + cst66xx_rst(); + msleep(50); + } + ret = hyn_wr_reg(hyn_66xxdata, 0xD0000400, 4, 0, 0); // disable lp i2c plu + mdelay(1); + ret = hyn_wr_reg(hyn_66xxdata, 0xD0000400, 4, 0, 0); + switch (mode) { + case NOMAL_MODE: + hyn_irq_set(hyn_66xxdata, ENABLE); + hyn_esdcheck_switch(hyn_66xxdata, ENABLE); + ret |= hyn_wr_reg(hyn_66xxdata, 0xD0000000, 4, 0, 0); + ret |= hyn_wr_reg(hyn_66xxdata, 0xD0000C00, 4, 0, 0); + ret |= hyn_wr_reg(hyn_66xxdata, 0xD0000100, 4, 0, 0); + break; + case GESTURE_MODE: + ret |= hyn_wr_reg(hyn_66xxdata, 0xD0000C01, 4, 0, 0); + break; + case LP_MODE: + ret |= hyn_wr_reg(hyn_66xxdata, 0xD00004AB, 4, 0, 0); + break; + case DIFF_MODE: + case RAWDATA_MODE: + case BASELINE_MODE: + case CALIBRATE_MODE: + ret |= hyn_wr_reg(hyn_66xxdata, 0xD00002AB, 4, 0, 0); + ret |= hyn_wr_reg(hyn_66xxdata, 0xD00001AB, 4, 0, 0); // enter debug mode + break; + case FAC_TEST_MODE: + ret |= hyn_wr_reg(hyn_66xxdata, 0xD00002AB, 4, 0, 0); + ret |= hyn_wr_reg(hyn_66xxdata, 0xD00000AB, 4, 0, 0); // enter fac test + break; + case DEEPSLEEP: + hyn_irq_set(hyn_66xxdata, DISABLE); + ret |= hyn_wr_reg(hyn_66xxdata, 0xD00022AB, 4, 0, 0); + default: + ret = -2; + break; + } + return ret; +} + +static int cst66xx_supend(void) +{ + ESP_LOGI(TAG, "touch sleep"); + + hyn_wr_reg(hyn_66xxdata, 0xD0000400, 4, 0, 0); // disable lp i2c plu + mdelay(1); + hyn_wr_reg(hyn_66xxdata, 0xD0000400, 4, 0, 0); + + hyn_irq_set(hyn_66xxdata, DISABLE); + hyn_wr_reg(hyn_66xxdata, 0xD00022AB, 4, 0, 0); // DEEPSLEEP + return 0; +} + +static int cst66xx_resum(void) +{ + ESP_LOGI(TAG, "enter %s", __func__); + cst66xx_rst(); + msleep(50); + + hyn_wr_reg(hyn_66xxdata, 0xD0000400, 4, 0, 0); // disable lp i2c plu + mdelay(1); + hyn_wr_reg(hyn_66xxdata, 0xD0000400, 4, 0, 0); + + hyn_irq_set(hyn_66xxdata, ENABLE); + hyn_wr_reg(hyn_66xxdata, 0xD0000000, 4, 0, 0); + hyn_wr_reg(hyn_66xxdata, 0xD0000C00, 4, 0, 0); + hyn_wr_reg(hyn_66xxdata, 0xD0000100, 4, 0, 0); + + return 0; +} + +static void cst66xx_rst(void) +{ + ESP_LOGI(TAG, "enter %s", __func__); + gpio_set_value(hyn_66xxdata->plat_data.reset_gpio, 0); + msleep(8); + gpio_set_value(hyn_66xxdata->plat_data.reset_gpio, 1); +} + +static int cst66xx_updata_tpinfo(void) +{ + u8 buf[60]; + struct tp_info *ic = &hyn_66xxdata->hw_info; + int ret = 0; + int retry = 5; + while (--retry) { + ret = 0; + // get all config info + ret |= cst66xx_set_workmode(NOMAL_MODE, ENABLE); + ret |= hyn_wr_reg(hyn_66xxdata, 0xD0030000, 0x80 | 4, buf, 50); + if (ret == 0 && buf[3] == 0xCA && buf[2] == 0xCA) + break; + mdelay(10); + ret |= hyn_wr_reg(hyn_66xxdata, 0xD0000400, 4, buf, 0); + } + + if (ret || retry == 0) { + ESP_LOGE(TAG, "cst66xx_updata_tpinfo failed"); + return -1; + } + + ic->fw_sensor_txnum = buf[48]; + ic->fw_sensor_rxnum = buf[49]; + ic->fw_key_num = buf[27]; + ic->fw_res_y = (buf[31] << 8) | buf[30]; + ic->fw_res_x = (buf[29] << 8) | buf[28]; + ESP_LOGI(TAG, "IC_info tx:%d rx:%d key:%d res-x:%d res-y:%d", ic->fw_sensor_txnum, ic->fw_sensor_rxnum, ic->fw_key_num, + ic->fw_res_x, ic->fw_res_y); + + ic->fw_project_id = U8TO32(buf[39], buf[38], buf[37], buf[36]); + ic->fw_chip_type = U8TO32(buf[3], buf[2], buf[1], buf[0]); + ic->fw_ver = U8TO32(buf[35], buf[34], buf[33], buf[32]); + ESP_LOGI(TAG, "IC_info fw_project_id:%04lx ictype:%04lx fw_ver:%04lx checksum:%04lx", ic->fw_project_id, ic->fw_chip_type, + ic->fw_ver, ic->ic_fw_checksum); + return 0; +} + +const struct hyn_ts_fuc cst66xx_fuc = { + .tp_rest = cst66xx_rst, + .tp_report = cst66xx_report, + .tp_supend = cst66xx_supend, + .tp_resum = cst66xx_resum, + .tp_chip_init = cst66xx_init, + .tp_updata_fw = NULL, + .tp_set_workmode = cst66xx_set_workmode, + .tp_check_esd = NULL, + .tp_prox_handle = NULL, + .tp_get_dbg_data = NULL, + .tp_get_test_result = NULL, +}; diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/hyn_i2c.c b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_i2c.c new file mode 100644 index 00000000000..35599a9e254 --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_i2c.c @@ -0,0 +1,87 @@ +#include "esp32-hal-i2c.h" +#include "hyn_core.h" +#include "hyn_platform.h" + +#define DATA_LENGTH 100 + +#define I2C_MASTER_NUM \ + 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */ +#define I2C_MASTER_FREQ_HZ 200000 /*!< I2C master clock frequency */ +#define I2C_MASTER_TIMEOUT_MS 1000 + +esp_err_t hyn_i2c_init(u8 pin_sda, u8 pin_scl) +{ + if (i2cIsInit(I2C_MASTER_NUM)) { + return ESP_OK; + } + + return i2cInit(I2C_MASTER_NUM, pin_sda, pin_scl, I2C_MASTER_FREQ_HZ); +} + +int hyn_write_data(struct hyn_ts_data *ts_data, u8 *buf, u8 reg_len, u16 len) +{ + (void)reg_len; + esp_err_t ret = i2cWrite(I2C_MASTER_NUM, ts_data->salve_addr, buf, len, I2C_MASTER_TIMEOUT_MS); + return ret == ESP_OK ? 0 : -1; +} + +int hyn_read_data(struct hyn_ts_data *ts_data, u8 *buf, u16 len) +{ + size_t readCount = 0; + esp_err_t ret = i2cRead(I2C_MASTER_NUM, ts_data->salve_addr, buf, len, I2C_MASTER_TIMEOUT_MS, &readCount); + return ret == ESP_OK && readCount == len ? 0 : -1; +} + +int hyn_wr_reg(struct hyn_ts_data *ts_data, u32 reg_addr, u8 reg_len, u8 *rbuf, u16 rlen) +{ + int ret = 0, i = 0; + u8 wbuf[4]; + reg_len = reg_len & 0x0F; + memset(wbuf, 0, sizeof(wbuf)); + i = reg_len; + while (i) { + i--; + wbuf[i] = reg_addr; + reg_addr >>= 8; + } + size_t readCount = 0; + if (rlen) { + ret = i2cWriteReadNonStop(I2C_MASTER_NUM, ts_data->salve_addr, wbuf, reg_len, rbuf, rlen, I2C_MASTER_TIMEOUT_MS, + &readCount); + return ret == ESP_OK && readCount == rlen ? 0 : -1; + } + + ret = i2cWrite(I2C_MASTER_NUM, ts_data->salve_addr, wbuf, reg_len, I2C_MASTER_TIMEOUT_MS); + return ret == ESP_OK ? 0 : -1; +} + +void hyn_delay_ms(int cnt) +{ + vTaskDelay(cnt / portTICK_PERIOD_MS); +} + +/**gpio ctl*/ +int gpio_set_value(uint32_t gpio_id, bool vlue) +{ + int handled = hyn_platform_gpio_set_value(gpio_id, vlue ? 1 : 0); + if (handled > 0) { + return 0; + } + if (handled < 0 || (int32_t)gpio_id < 0) { + return -1; + } + gpio_set_level(gpio_id, vlue); + return 0; +} +bool gpio_get_value(uint32_t gpio_id) +{ + int value = 0; + int handled = hyn_platform_gpio_get_value(gpio_id, &value); + if (handled > 0) { + return value ? true : false; + } + if (handled < 0 || (int32_t)gpio_id < 0) { + return false; + } + return gpio_get_level(gpio_id); +} diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/hyn_platform.cpp b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_platform.cpp new file mode 100644 index 00000000000..40049180bb9 --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_platform.cpp @@ -0,0 +1,90 @@ +#include "hyn_platform.h" + +#include +#include + +#include + +#include "ExtensionIOXL9555.hpp" +#include "HynTouch.h" + +namespace +{ + +constexpr const char *kTag = "HynTouch"; + +HynTouchVirtualGpioWriteCallback g_gpio_write_callback = nullptr; +HynTouchVirtualGpioReadCallback g_gpio_read_callback = nullptr; +void *g_gpio_callback_user_data = nullptr; +ExtensionIOXL9555 *g_xl9555 = nullptr; + +bool write_virtual_xl9555_gpio(uint32_t gpio_id, bool value) +{ + if (!g_xl9555 || !XL9555_GPIO_IS((int)gpio_id)) { + return false; + } + + const uint8_t pin = XL9555_GPIO_TO_PIN(gpio_id); + g_xl9555->pinMode(pin, OUTPUT); + g_xl9555->digitalWrite(pin, value ? HIGH : LOW); + return true; +} + +bool read_virtual_xl9555_gpio(uint32_t gpio_id, int *out_value) +{ + if (!g_xl9555 || !out_value || !XL9555_GPIO_IS((int)gpio_id)) { + return false; + } + + const uint8_t pin = XL9555_GPIO_TO_PIN(gpio_id); + *out_value = g_xl9555->digitalRead(pin) ? 1 : 0; + return true; +} + +} // namespace + +int hyn_platform_gpio_set_value(uint32_t gpio_id, int value) +{ + if (g_gpio_write_callback && g_gpio_write_callback(gpio_id, value != 0, g_gpio_callback_user_data)) { + return 1; + } + if (write_virtual_xl9555_gpio(gpio_id, value != 0)) { + return 1; + } + if (XL9555_GPIO_IS((int)gpio_id)) { + ESP_LOGE(kTag, "Virtual GPIO %lu needs a handler", (unsigned long)gpio_id); + return -1; + } + return 0; +} + +int hyn_platform_gpio_get_value(uint32_t gpio_id, int *out_value) +{ + if (!out_value) { + return -1; + } + if (g_gpio_read_callback && g_gpio_read_callback(gpio_id, out_value, g_gpio_callback_user_data)) { + return 1; + } + if (read_virtual_xl9555_gpio(gpio_id, out_value)) { + return 1; + } + if (XL9555_GPIO_IS((int)gpio_id)) { + ESP_LOGE(kTag, "Virtual GPIO %lu needs a handler", (unsigned long)gpio_id); + return -1; + } + return 0; +} + +void hyn_touch_set_virtual_gpio_callbacks(HynTouchVirtualGpioWriteCallback write_callback, + HynTouchVirtualGpioReadCallback read_callback, void *user_data) +{ + g_gpio_write_callback = write_callback; + g_gpio_read_callback = read_callback; + g_gpio_callback_user_data = user_data; +} + +void hyn_touch_attach_xl9555(ExtensionIOXL9555 *io) +{ + g_xl9555 = io; +} diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/hyn_platform.h b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_platform.h new file mode 100644 index 00000000000..7df2142f0a5 --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_platform.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Platform hook for the HYN touch stack. +// Returns 1 if handled, 0 if the caller should fall back to a normal GPIO, +// or -1 if the GPIO is virtual but no handler is available. +int hyn_platform_gpio_set_value(uint32_t gpio_id, int value); +int hyn_platform_gpio_get_value(uint32_t gpio_id, int *out_value); + +#ifdef __cplusplus +} +#endif diff --git a/variants/esp32s3/t-deck-max/HynTouch/src/hyn_ts_ext.c b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_ts_ext.c new file mode 100644 index 00000000000..66efb5517bc --- /dev/null +++ b/variants/esp32s3/t-deck-max/HynTouch/src/hyn_ts_ext.c @@ -0,0 +1,75 @@ + +#include "hyn_core.h" + +void hyn_irq_set(struct hyn_ts_data *ts_data, u8 value) +{ + // HYN_ENTER(); +} + +void hyn_set_i2c_addr(struct hyn_ts_data *ts_data, u8 addr) +{ + ts_data->salve_addr = addr; +} + +u16 hyn_sum16(int val, u8 *buf, u16 len) +{ + u16 sum = val; + while (len--) + sum += *buf++; + return sum; +} + +u32 hyn_sum32(int val, u32 *buf, u16 len) +{ + u32 sum = val; + while (len--) + sum += *buf++; + return sum; +} + +// int hyn_u8_extend_u16(u8 *des_ptr, u16 len) +// { +// u8 *src_ptr = des_ptr+len; +// while(--len){ +// *des_ptr++ = *src_ptr++; +// *des_ptr++ = 0; +// } +// } + +void hyn_esdcheck_switch(struct hyn_ts_data *ts_data, u8 enable) {} + +int copy_for_updata(struct hyn_ts_data *ts_data, u8 *buf, u32 offset, u16 len) +{ + int ret = -1; + + return ret; +} + +int hyn_wait_irq_timeout(struct hyn_ts_data *ts_data, int msec) +{ + ts_data->hyn_irq_flg = 0; + while (msec--) { + msleep(1); + if (ts_data->hyn_irq_flg == 1) { + ts_data->hyn_irq_flg = 0; + msec = -1; + break; + } + } + return msec == -1 ? 0 : -1; +} + +static int hyn_get_threshold(char *filename, char *match_string, s16 *pstore, u16 len) +{ + return -1; +} + +int factory_multitest(struct hyn_ts_data *ts_data, char *cfg_path, u8 *data, s16 *test_th, u8 test_item) +{ + return -1; +} + +int fac_test_log_save(char *log_name, struct hyn_ts_data *ts_data, s16 *test_data, int test_ret, u8 test_item) +{ + return 0; +} diff --git a/variants/esp32s3/t-deck-max/TDeckMaxBoard.h b/variants/esp32s3/t-deck-max/TDeckMaxBoard.h new file mode 100644 index 00000000000..3155c274269 --- /dev/null +++ b/variants/esp32s3/t-deck-max/TDeckMaxBoard.h @@ -0,0 +1,139 @@ +#pragma once + +#include +#include + +// Board identity +#define BOARD_NAME "T-Deck-MAX v0.3" +#define UI_T_DECK_PRO_VERSION "v3.1-260313" // Software version +#define BOARD_T_DECK_PRO_VERSION "v3.3-250911" // Hardware version + +// Common serial aliases used by multiple examples. +#define SerialMon Serial +#define SerialAT Serial1 +#define SerialGPS Serial2 + +// I2C addresses +// Note: later T-Deck-MAX revisions use SY6970 instead of BQ25896. +#define BOARD_I2C_ADDR_ES8311 0x18 // ES8311 audio codec +#define BOARD_I2C_ADDR_TOUCH 0x1A // CST328 touch controller +#define BOARD_I2C_ADDR_XL9555 0x20 // XL9555 IO expander +#define BOARD_I2C_ADDR_GYROSCOPDE 0x28 // BHI260AP gyroscope +#define BOARD_I2C_ADDR_KEYBOARD 0x34 // TCA8418 keyboard matrix controller +#define BOARD_I2C_ADDR_BQ27220 0x55 // BQ27220 fuel gauge +#define BOARD_I2C_ADDR_MOTOR 0x5A // DRV2605 vibration motor driver +#define BOARD_I2C_ADDR_BQ25896 0x6B // BQ25896 charger (deprecated) +#define BOARD_I2C_ADDR_SY6970 0x6A // SY6970 charger + +// Common I2C bus +#define BOARD_I2C_SDA 13 +#define BOARD_I2C_SCL 14 + +// XL9555 IO expansion +#define BOARD_XL9555_INT (-1) // XL9555 INT is not connected to the ESP32-S3 +#define BOARD_XL9555_SDA BOARD_I2C_SDA +#define BOARD_XL9555_SCL BOARD_I2C_SCL +#define BOARD_XL9555_00_6609_EN (0) // HIGH: enable A7682E power +#define BOARD_XL9555_01_LORA_EN (1) // HIGH: enable SX1262 power +#define BOARD_XL9555_02_GPS_EN (2) // HIGH: enable GPS power +#define BOARD_XL9555_03_1V8_EN (3) // HIGH: enable BHI260AP power +#define BOARD_XL9555_04_LORA_SEL (4) // HIGH: internal antenna, LOW: external antenna +#define BOARD_XL9555_05_MOTOR_EN (5) // HIGH: enable DRV2605 power +#define BOARD_XL9555_06_AMPLIFIER (6) // HIGH: enable power amplifier +#define BOARD_XL9555_07_TOUCH_RST (7) // LOW: reset touch +#define BOARD_XL9555_10_PWRKEY_EN (8) // HIGH: enable A7682E POWERKEY +#define BOARD_XL9555_11_KEY_RST (9) // LOW: reset keyboard +#define BOARD_XL9555_12_AUDIO_SEL (10) // HIGH: A7682E audio, LOW: ES8311 audio +#define BOARD_XL9555_13 (11) // reserved +#define BOARD_XL9555_14 (12) // reserved +#define BOARD_XL9555_15 (13) // reserved +#define BOARD_XL9555_16 (14) // reserved +#define BOARD_XL9555_17 (15) // reserved + +// XL9555 "virtual GPIO" encoding for drivers that expect a GPIO number. +#define XL9555_GPIO_BASE (0x100) +#define XL9555_GPIO(pin) (XL9555_GPIO_BASE + (pin)) +#define XL9555_GPIO_IS(id) ((int)(id) >= XL9555_GPIO_BASE && (int)(id) < (XL9555_GPIO_BASE + 16)) +#define XL9555_GPIO_TO_PIN(id) ((uint8_t)((id)-XL9555_GPIO_BASE)) + +// Keyboard +#define BOARD_KEYBOARD_SCL BOARD_I2C_SCL +#define BOARD_KEYBOARD_SDA BOARD_I2C_SDA +#define BOARD_KEYBOARD_INT 15 +#define BOARD_KEYBOARD_LED 42 +#define BOARD_KEYBOARD_RST BOARD_XL9555_11_KEY_RST + +// Touch +#define BOARD_TOUCH_SCL BOARD_I2C_SCL +#define BOARD_TOUCH_SDA BOARD_I2C_SDA +#define BOARD_TOUCH_INT 12 +#define BOARD_TOUCH_RST XL9555_GPIO(BOARD_XL9555_07_TOUCH_RST) + +// Gyroscope +#define BOARD_GYROSCOPDE_SCL BOARD_I2C_SCL +#define BOARD_GYROSCOPDE_SDA BOARD_I2C_SDA +#define BOARD_GYROSCOPDE_INT 21 +#define BOARD_GYROSCOPDE_RST -1 + +// Motor +#define BOARD_MOTOR_SCL BOARD_I2C_SCL +#define BOARD_MOTOR_SDA BOARD_I2C_SDA +#define BOARD_MOTOR_EN BOARD_XL9555_05_MOTOR_EN + +// ES8311 +#define BOARD_ES8311_SCL BOARD_I2C_SCL +#define BOARD_ES8311_SDA BOARD_I2C_SDA +#define BOARD_ES8311_MCLK 38 +#define BOARD_ES8311_SCLK 39 +#define BOARD_ES8311_ASDOUT 40 +#define BOARD_ES8311_LRCK 18 +#define BOARD_ES8311_DSDIN 17 + +// Shared SPI bus +#define BOARD_SPI_SCK 36 +#define BOARD_SPI_MOSI 33 +#define BOARD_SPI_MISO 47 + +// Display +#define LCD_HOR_SIZE 240 +#define LCD_VER_SIZE 320 +#define DISP_BUF_SIZE (LCD_HOR_SIZE * LCD_VER_SIZE) + +#define BOARD_EPD_BL 41 +#define BOARD_EPD_SCK BOARD_SPI_SCK +#define BOARD_EPD_MOSI BOARD_SPI_MOSI +#define BOARD_EPD_DC 35 +#define BOARD_EPD_CS 34 +#define BOARD_EPD_BUSY 37 +#define BOARD_EPD_RST 9 + +// SD card +#define BOARD_SD_CS 48 +#define BOARD_SD_SCK BOARD_SPI_SCK +#define BOARD_SD_MOSI BOARD_SPI_MOSI +#define BOARD_SD_MISO BOARD_SPI_MISO + +// LoRa +#define BOARD_LORA_SCK BOARD_SPI_SCK +#define BOARD_LORA_MOSI BOARD_SPI_MOSI +#define BOARD_LORA_MISO BOARD_SPI_MISO +#define BOARD_LORA_CS 3 +#define BOARD_LORA_BUSY 6 +#define BOARD_LORA_RST 4 +#define BOARD_LORA_INT 5 + +// GPS +#define BOARD_GPS_RXD 2 +#define BOARD_GPS_TXD 16 +#define BOARD_GPS_PPS 1 + +// A7682E modem +#define BOARD_A7682E_RI 7 +#define BOARD_A7682E_ITR 8 // DTR +#define BOARD_A7682E_DTR BOARD_A7682E_ITR +#define BOARD_A7682E_RXD 10 +#define BOARD_A7682E_TXD 11 +#define BOARD_A7682E_PWRKEY BOARD_XL9555_10_PWRKEY_EN + +// Boot pin +#define BOARD_BOOT_PIN 0 diff --git a/variants/esp32s3/t-deck-max/pins_arduino.h b/variants/esp32s3/t-deck-max/pins_arduino.h new file mode 100644 index 00000000000..93b3e47e742 --- /dev/null +++ b/variants/esp32s3/t-deck-max/pins_arduino.h @@ -0,0 +1,17 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +static const uint8_t SDA = 13; +static const uint8_t SCL = 14; + +static const uint8_t SS = 3; +static const uint8_t MOSI = 33; +static const uint8_t MISO = 47; +static const uint8_t SCK = 36; + +#endif /* Pins_Arduino_h */ diff --git a/variants/esp32s3/t-deck-max/platformio.ini b/variants/esp32s3/t-deck-max/platformio.ini new file mode 100644 index 00000000000..860b5e180d4 --- /dev/null +++ b/variants/esp32s3/t-deck-max/platformio.ini @@ -0,0 +1,50 @@ +[env:t-deck-max] +custom_meshtastic_hw_model = 139 +custom_meshtastic_hw_model_slug = T_DECK_MAX +custom_meshtastic_architecture = esp32-s3 +custom_meshtastic_actively_supported = true +custom_meshtastic_support_level = 1 +custom_meshtastic_display_name = LILYGO T-Deck Max +custom_meshtastic_images = tdeck_pro.svg +custom_meshtastic_tags = LilyGo +custom_meshtastic_requires_dfu = true +custom_meshtastic_partition_scheme = 16MB +custom_meshtastic_has_mui = false + +extends = esp32s3_base +board = t-deck-pro +board_check = true +upload_protocol = esptool + +build_src_filter = + ${esp32s3_base.build_src_filter} + +<../variants/esp32s3/t-deck-max> + +build_flags = + ${esp32s3_base.build_flags} -I variants/esp32s3/t-deck-max -I variants/esp32s3/t-deck-max/HynTouch/src + -D T_DECK_MAX + -D BOARD_HAS_PSRAM + -D USE_EINK + -D EINK_DISPLAY_MODEL=GxEPD2_310_GDEQ031T10 + -D EINK_WIDTH=240 + -D EINK_HEIGHT=320 + -D EINK_LIMIT_FASTREFRESH=10 + -D EINK_LIMIT_GHOSTING_PX=2000 + -D EINK_NOT_HIBERNATE + +lib_deps = + ${esp32s3_base.lib_deps} + # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 + zinggjm/GxEPD2@1.6.9 + # renovate: datasource=custom.pio depName=Adafruit DRV2605 packageName=adafruit/library/Adafruit DRV2605 Library + adafruit/Adafruit DRV2605 Library@1.2.4 + # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib + lewisxhe/SensorLib@0.3.4 + # renovate: datasource=github-tags depName=pschatzmann_arduino-audio-driver packageName=pschatzmann/arduino-audio-driver + https://github.com/pschatzmann/arduino-audio-driver/archive/v0.2.1.zip + # renovate: datasource=git-refs depName=ESP8266Audio packageName=https://github.com/meshtastic/ESP8266Audio gitBranch=meshtastic-2.0.0-dacfix + https://github.com/meshtastic/ESP8266Audio/archive/343024632ee78d6216907b2353fc943a62422d80.zip + # renovate: datasource=custom.pio depName=ESP8266SAM packageName=earlephilhower/library/ESP8266SAM + earlephilhower/ESP8266SAM@1.1.0 + # renovate: datasource=git-refs depName=BQ27220 packageName=https://github.com/mverch67/BQ27220 gitBranch=main + https://github.com/mverch67/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip diff --git a/variants/esp32s3/t-deck-max/variant.cpp b/variants/esp32s3/t-deck-max/variant.cpp new file mode 100644 index 00000000000..60109d9f2bb --- /dev/null +++ b/variants/esp32s3/t-deck-max/variant.cpp @@ -0,0 +1,45 @@ +#include "variant.h" +#include "ExtensionIOXL9555.hpp" + +extern ExtensionIOXL9555 io; + +static void setExpandPin(uint8_t pin, uint8_t value) +{ + io.pinMode(pin, OUTPUT); + io.digitalWrite(pin, value); +} + +static void pulseExpandPinLow(uint8_t pin, uint32_t lowMs, uint32_t highMs) +{ + setExpandPin(pin, LOW); + delay(lowMs); + io.digitalWrite(pin, HIGH); + delay(highMs); +} + +void earlyInitVariant() +{ + pinMode(LORA_CS, OUTPUT); + digitalWrite(LORA_CS, HIGH); + pinMode(SDCARD_CS, OUTPUT); + digitalWrite(SDCARD_CS, HIGH); + pinMode(PIN_EINK_CS, OUTPUT); + digitalWrite(PIN_EINK_CS, HIGH); + pinMode(KB_INT, INPUT_PULLUP); + pinMode(CST328_PIN_INT, INPUT_PULLUP); + pinMode(PIN_EINK_BL, OUTPUT); + analogWrite(PIN_EINK_BL, 0); + + io.begin(Wire, XL9555_SLAVE_ADDRESS0, SDA, SCL); + setExpandPin(EXPANDS_MODEM_EN, LOW); + setExpandPin(EXPANDS_MODEM_PWRKEY, LOW); + setExpandPin(EXPANDS_LORA_EN, HIGH); + setExpandPin(EXPANDS_LORA_SEL, HIGH); + setExpandPin(EXPANDS_GPS_EN, HIGH); + setExpandPin(EXPANDS_1V8_EN, HIGH); + setExpandPin(EXPANDS_DRV_EN, HIGH); + setExpandPin(EXPANDS_AMP_EN, LOW); + setExpandPin(EXPANDS_AUDIO_SEL, LOW); + pulseExpandPinLow(EXPANDS_TOUCH_RST, 20, 60); + pulseExpandPinLow(EXPANDS_KB_RST, 20, 60); +} diff --git a/variants/esp32s3/t-deck-max/variant.h b/variants/esp32s3/t-deck-max/variant.h new file mode 100644 index 00000000000..accca27ab9f --- /dev/null +++ b/variants/esp32s3/t-deck-max/variant.h @@ -0,0 +1,118 @@ +#pragma once + +#include "TDeckMaxBoard.h" + +// Display (E-Ink) +#define PIN_EINK_CS BOARD_EPD_CS +#define PIN_EINK_BUSY BOARD_EPD_BUSY +#define PIN_EINK_DC BOARD_EPD_DC +#define PIN_EINK_RES BOARD_EPD_RST +#define PIN_EINK_SCLK BOARD_EPD_SCK +#define PIN_EINK_MOSI BOARD_EPD_MOSI +// This board has a PWM frontlight, unlike older PIN_EINK_BL users where the pin can mean panel power. +#define HAS_EINK_FRONTLIGHT +#define PIN_EINK_BL BOARD_EPD_BL +#define BRIGHTNESS_DEFAULT 130 + +#define I2C_SDA SDA +#define I2C_SCL SCL + +// CST328 touch screen (implementation in src/platform/extra_variants/t_deck_max/variant.cpp) +#define HAS_TOUCHSCREEN 1 +#define CST328_PIN_INT BOARD_TOUCH_INT +#define SCREEN_TOUCH_INT BOARD_TOUCH_INT +// Keep CST328 usable while awake, but don't use its IRQ as a light-sleep wake source. +// On T-Deck Max, touch wake after the 120s light-sleep cycle can reboot the ESP32-S3. + +#define USE_POWERSAVE +#define SLEEP_TIME 120 + +// GNSS +#define HAS_GPS 1 +#define GPS_BAUDRATE 38400 +#define GPS_RX_PIN BOARD_GPS_RXD +#define GPS_TX_PIN BOARD_GPS_TXD +#define PIN_GPS_PPS BOARD_GPS_PPS + +#define BUTTON_PIN BOARD_BOOT_PIN + +// Vibration motor +#define HAS_DRV2605 1 + +// SPI interface SD card slot +#define HAS_SDCARD +#define SDCARD_USE_SPI1 +#define SPI_MOSI BOARD_SPI_MOSI +#define SPI_SCK BOARD_SPI_SCK +#define SPI_MISO BOARD_SPI_MISO +#define SPI_CS BOARD_SD_CS +#define SDCARD_CS SPI_CS +#define SD_SPI_FREQUENCY 75000000U + +// TCA8418 keyboard +#define KB_BL_PIN BOARD_KEYBOARD_LED +#define KB_INT BOARD_KEYBOARD_INT +#define CANNED_MESSAGE_MODULE_ENABLE 1 + +// Audio codec ES8311 +#define HAS_I2S +#define DAC_I2S_BCK BOARD_ES8311_SCLK +#define DAC_I2S_WS BOARD_ES8311_LRCK +#define DAC_I2S_DOUT BOARD_ES8311_DSDIN +#define DAC_I2S_DIN BOARD_ES8311_ASDOUT +#define DAC_I2S_MCLK BOARD_ES8311_MCLK + +// Gyroscope BHI260AP +#define HAS_BHI260AP + +// Battery charger SY6970 and fuel gauge BQ27220 +#define HAS_PPM 1 +#define XPOWERS_CHIP_SY6970 +#define HAS_BQ27220 1 +#define BQ27220_I2C_SDA SDA +#define BQ27220_I2C_SCL SCL +#define BQ27220_DESIGN_CAPACITY 1500 + +// External expansion chip XL9555 +#define USE_XL9555 +#define EXPANDS_MODEM_EN BOARD_XL9555_00_6609_EN +#define EXPANDS_LORA_EN BOARD_XL9555_01_LORA_EN +#define EXPANDS_GPS_EN BOARD_XL9555_02_GPS_EN +#define EXPANDS_1V8_EN BOARD_XL9555_03_1V8_EN +#define EXPANDS_LORA_SEL BOARD_XL9555_04_LORA_SEL +#define EXPANDS_DRV_EN BOARD_XL9555_05_MOTOR_EN +#define EXPANDS_AMP_EN BOARD_XL9555_06_AMPLIFIER +#define EXPANDS_TOUCH_RST BOARD_XL9555_07_TOUCH_RST +#define EXPANDS_MODEM_PWRKEY BOARD_XL9555_10_PWRKEY_EN +#define EXPANDS_KB_RST BOARD_XL9555_11_KEY_RST +#define EXPANDS_AUDIO_SEL BOARD_XL9555_12_AUDIO_SEL + +// LoRa +#define USE_SX1262 +#define USE_SX1268 + +#define LORA_SCK BOARD_LORA_SCK +#define LORA_MISO BOARD_LORA_MISO +#define LORA_MOSI BOARD_LORA_MOSI +#define LORA_CS BOARD_LORA_CS + +#define LORA_DIO0 -1 +#define LORA_RESET BOARD_LORA_RST +#define LORA_DIO1 BOARD_LORA_INT +#define LORA_DIO2 BOARD_LORA_BUSY +#define LORA_DIO3 + +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 2.4 + +// A7682E modem pins are defined but the expander-controlled rail stays off by default. +#define MODEM_RI BOARD_A7682E_RI +#define MODEM_DTR BOARD_A7682E_DTR +#define MODEM_RX BOARD_A7682E_RXD +#define MODEM_TX BOARD_A7682E_TXD + +#define HAS_PHYSICAL_KEYBOARD 1