From f518d8228ca63206930864ab46445f8e3428de9e Mon Sep 17 00:00:00 2001 From: Christoph Honal Date: Mon, 13 May 2024 16:03:10 +0200 Subject: [PATCH 1/4] LVGL: Add SetNewTap to generate tap events This function sends a tap down and tap up event to LVGL, despite being called only once. --- src/displayapp/LittleVgl.cpp | 12 ++++++++++++ src/displayapp/LittleVgl.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/displayapp/LittleVgl.cpp b/src/displayapp/LittleVgl.cpp index 89893cf79d..3545e8bd42 100644 --- a/src/displayapp/LittleVgl.cpp +++ b/src/displayapp/LittleVgl.cpp @@ -260,11 +260,23 @@ void LittleVgl::CancelTap() { } } +void LittleVgl::SetNewTap(int16_t x, int16_t y) { + touchPoint = {x, y}; + tapped = true; + simulate_tap_release = true; +} + bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) { ptr->point.x = touchPoint.x; ptr->point.y = touchPoint.y; if (tapped) { ptr->state = LV_INDEV_STATE_PR; + if (simulate_tap_release) { + // If a tap consists of only a single event, enqueue a synthetic release state update + tapped = false; + simulate_tap_release = false; + return true; + } } else { ptr->state = LV_INDEV_STATE_REL; } diff --git a/src/displayapp/LittleVgl.h b/src/displayapp/LittleVgl.h index 9a15ae1599..9e5b54ca54 100644 --- a/src/displayapp/LittleVgl.h +++ b/src/displayapp/LittleVgl.h @@ -26,6 +26,7 @@ namespace Pinetime { void SetFullRefresh(FullRefreshDirections direction); void SetNewTouchPoint(int16_t x, int16_t y, bool contact); void CancelTap(); + void SetNewTap(int16_t x, int16_t y); bool GetFullRefresh() { bool returnValue = fullRefresh; @@ -65,6 +66,7 @@ namespace Pinetime { lv_point_t touchPoint = {}; bool tapped = false; bool isCancelled = false; + bool simulate_tap_release = false; }; } } From b29459989f91663d5ce59c0c200f9c1d3f5369c6 Mon Sep 17 00:00:00 2001 From: Christoph Honal Date: Tue, 10 May 2022 23:05:23 +0200 Subject: [PATCH 2/4] CST816S: Add register description header file --- src/CMakeLists.txt | 1 + src/drivers/Cst816s_registers.h | 162 ++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 src/drivers/Cst816s_registers.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b27c19de7..5ad7020a3f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -652,6 +652,7 @@ set(INCLUDE_FILES components/timer/Timer.h components/alarm/AlarmController.h drivers/Cst816s.h + drivers/Cst816s_registers.h FreeRTOS/portmacro.h FreeRTOS/portmacro_cmsis.h displayapp/LittleVgl.h diff --git a/src/drivers/Cst816s_registers.h b/src/drivers/Cst816s_registers.h new file mode 100644 index 0000000000..edec944fde --- /dev/null +++ b/src/drivers/Cst816s_registers.h @@ -0,0 +1,162 @@ +#pragma once + +// CST816(S) / CST716 High-performance self-capacitive touch chip +// Hynitron, www.hynitron.com + +// Manually assembled from auto-translated incomplete datasheets and documents. + +// The device contains an embedded firmware, behaviour may differ. +// Code fragments on how to update the internal firmware exist, but not much is know about +// the internal architecture. No verified compatible binary firmware has been found yet. + +// The "S" suffix stands for speed, i.e. double the sample rate (>100 Hz instead of ~50 Hz (?)). + +// The CST816S has an auto-sleep low-power mode, from which it can wake via a touch event. +// In low-power sleep, the I2C interface is inactive. +// It sends a gesture event while the gesture is still being performed. + +// The CST716 has no auto-sleep functionality, and sends a gesture event after the gesture has been completed. +// Its I2C interface is always active, as it stays in normal mode. + +// Both chips are able to enter a deep-sleep mode, from which they can only wake via a signal on the reset pin. + +// Registers + +// * = Works only on chips which have not been preconfigured (fused) in the factory. +// CST716 are probably always fused, default: reporting mode. +// CST816S can be reconfigureable, but can be fused as well. Some fused ones report with a CST716 chip ID. + +// ** = Auto-sleep related registers. Only on the CST816S. + +#define DEV_MODE 0x00 // readonly. Always 0. +#define GESTURE_ID 0x01 // readonly. Which gesture was detected. +#define TD_STATUS 0x02 // readonly. Number of touch points. (0 - 2). + +#define P1_X_POS_H 0x03 // readonly. Point 1: X touch event flag + X touch coordinate MSB. +#define P1_X_POS_L 0x04 // readonly. Point 1: X touch coordinate LSB. +#define P1_Y_POS_H 0x05 // readonly. Point 1: Y touch event flag + Y touch coordinate MSB. +#define P1_Y_POS_L 0x06 // readonly. Point 1: Y touch coordinate LSB. +#define P1_WEIGHT 0x07 // readonly. Point 1: Touch weight. +#define P1_MISC 0x08 // readonly. Point 1: Touch area. + +#define P2_X_POS_H 0x09 // readonly. Point 2: X touch event flag + X touch coordinate MSB. +#define P2_X_POS_L 0x0A // readonly. Point 2: X touch coordinate LSB. +#define P2_Y_POS_H 0x0B // readonly. Point 2: Y touch event flag + Y touch coordinate MSB. +#define P2_Y_POS_L 0x0C // readonly. Point 2: Y touch coordinate LSB. +#define P2_WEIGHT 0x0D // readonly. Point 2: Touch weight. +#define P2_MISC 0x0E // readonly. Point 2: Touch area. + +#define BPC0_H 0xB0 // unknown. BPC0 value MSB. +#define BPC0_L 0xB1 // unknown. BPC0 value LSB. +#define BPC1_H 0xB2 // unknown. BPC1 value MSB. +#define BPC1_L 0xB3 // unknown. BPC1 value LSB. + +#define CHIP_ID 0xA7 // readonly. Chip / firmware type ID. +#define PROJ_ID 0xA8 // readonly. Vendor / project ID. +#define FW_VERSION 0xA9 // readonly. Firmware version. + +#define MOTION_MASK 0xEC // read-write*. Motion configuration. +#define IRQ_PULSE_WIDTH 0xED // read-write. Interrupt low pulse output width. Unit 0.1ms, possible value: 1~200. The default value is 10. +// read-write. Normal fast detection cycle. This value affects LpAutoWakeTime and AutoSleepTime. Unit 10ms, possible value: 1~30. The +// default value is 1. +#define NOR_SCAN_PER 0xEE +// read-write. Gesture detection sliding partition angle control. Angle=tan(c)*10. c is the angle based on the positive direction of +// the x-axis. +#define MOTION_S1_ANGLE 0xEF + +#define LP_SCAN_RAW1_H 0xF0 // readonly**. Low power scan, MSB of the reference value of channel 1. +#define LP_SCAN_RAW1_L 0xF1 // readonly**. Low power scan, LSB of the reference value of channel 1. +#define LP_SCAN_RAW2_H 0xF2 // readonly**. Low power scan, MSB of the reference value of channel 2. +#define LP_SCAN_RAW2_L 0xF3 // readonly**. Low power scan, LSB of the reference value of channel 2. +// read-write**. Automatic recalibration cycle in low power mode. The unit is 1 minute, and the possible value is 1 to 5. The default +// value is 5. +#define LP_AUTO_WAKE_TIME 0xF4 +// read-write**. Low-power scan wake-up threshold. The smaller the more sensitive. Possible values: 1 to 255. The default value +// is 48. +#define LP_SCAN_TH 0xF5 +// read-write**. Low power scan range. The larger the more sensitive, the higher the power consumption. Possible values: 0, 1, 2, 3. +// The default value is 3. +#define LP_SCAN_WIN 0xF6 +// read-write**. Low power scan frequency. The smaller the more sensitive. Possible values: 1 to 255. The default value is 7. +#define LP_SCAN_FREQ 0xF7 +#define LP_SCAN_I_DAC 0xF8 // read-write**. Low power scan current. The smaller the more sensitive. Possible values: 1 to 255. +// read-write**. Automatically enter low power mode when there is no touch for x seconds. The unit is 1s, the default value is 2. +#define AUTO_SLEEP_TIME 0xF9 +// read-write*. Interrupt configuration. 0x60 = report mode, 0x11 = gesture mode, 0x71 = both. +#define IRQ_CTL 0xFA +// read-write. Automatic reset (cancel) when there is a touch but no valid gesture within x seconds. The unit is 1s. When it is 0, +// this function is not enabled. Default is 5. +#define AUTO_RESET 0xFB +// read-write. Automatic reset (cancel) after long press for x seconds. The unit is 1s. When it is 0, this function is not enabled. +// Default is 10. +#define LONG_PRESS_TIME 0xFC +// read-write. Pin IO configuration. +#define IO_CTL 0xFD +// read-write**. The default is 0, enabling automatic entry into low power mode. When it is a non-zero value, automatic entry into +// low power mode is disabled. +#define DIS_AUTO_SLEEP 0xFE + +#define PWR_MODE_CST816S 0xE5 // read-write. Power state for the CST816S. +#define PWR_MODE_CST716 0xA5 // read-write. Power state for the CST716. + +// Data fields + +// GESTURE_ID + +#define GESTURE_ID_NONE 0x00 +#define GESTURE_ID_SLIDE_DOWN 0x01 +#define GESTURE_ID_SLIDE_UP 0x02 +#define GESTURE_ID_SLIDE_LEFT 0x03 +#define GESTURE_ID_SLIDE_RIGHT 0x04 +#define GESTURE_ID_SINGLE_TAP 0x05 +#define GESTURE_ID_DOUBLE_TAP 0x0B +#define GESTURE_ID_LONG_PRESS 0x0C + +// TD_STATUS + +#define TD_STATUS_MASK 0x0F + +// *_POS_H + +#define POS_H_POS_MASK 0x0F + +#define POS_H_EVENT_MASK 0xC0 +#define POS_H_EVENT0 (1 << 6) +#define POS_H_EVENT1 (1 << 7) + +#define POS_H_EVENT_DOWN (0 << 6) +#define POS_H_EVENT_UP (1 << 6) +#define POS_H_EVENT_CONTACT (2 << 6) +#define POS_H_EVENT_NONE (3 << 6) + +// CHIP_ID + +#define CHIP_ID_CST816S 0xB4 +#define CHIP_ID_CST716 0x20 + +// Control register configurations + +// MOTION_MASK + +#define MOTION_MASK_EN_DCLICK (1 << 0) // Enable double click action +#define MOTION_MASK_EN_CON_UD (1 << 1) // Enable continuous up-down sliding action +#define MOTION_MASK_EN_CON_LR (1 << 2) // Enable continuous left-right sliding action + +// IRQ_CTL + +#define IRQ_CTL_ONCE_WLP (1 << 0) // The long press gesture only emits a low pulse signal. +#define IRQ_CTL_EN_MOTION (1 << 4) // When a gesture is detected, a low pulse is emitted. +#define IRQ_CTL_EN_CHANGE (1 << 5) // A low pulse is emitted when a touch state change is detected. +#define IRQ_CTL_EN_TOUCH (1 << 6) // Periodically emit low pulses when a touch is detected. +#define IRQ_CTL_EN_TEST (1 << 7) // Interrupt pin test, automatically and periodically send low pulses after enabling. + +// IO_CTL + +#define IO_CTL_EN_1V8 (1 << 0) // I2C and IRQ pin voltage level selection, the default is VDD level. 0: VDD, 1: 1.8V. +#define IO_CTL_IIC_OD (1 << 1) // I2C pin drive mode, the default is resistor pull-up. 0: Resistor pull-up 1: OD. +#define IO_CTL_SOFT_RST (1 << 2) // Enable soft reset by pulling down the IRQ pin. 0: Soft reset is disabled. 1: Enable soft reset. + +// PWR_MODE_* + +#define PWR_MODE_ACTIVE 0x00 // Normal state of execution +#define PWR_MODE_DEEP_SLEEP 0x03 // Deep-sleep mode. Touch to wakeup and I2C are disabled. From 167950a7ba2a1687b171de859dff763748160906 Mon Sep 17 00:00:00 2001 From: Christoph Honal Date: Mon, 13 May 2024 16:07:10 +0200 Subject: [PATCH 3/4] Add CST716 touch support and fused mode support The P8 smartwatches use the CST716 or CST816S chips in various modes. The device ID of these chips cannot be used for runtime detection, because it does not give the hardware revision / firmware configuration. --- src/CMakeLists.txt | 5 + src/drivers/Cst816s.cpp | 151 +++++++++++++----------------- src/drivers/Cst816s.h | 35 +++---- src/systemtask/SystemTask.cpp | 8 +- src/touchhandler/TouchHandler.cpp | 50 +++++++++- 5 files changed, 137 insertions(+), 112 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5ad7020a3f..915e095bd1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -795,23 +795,28 @@ add_definitions(-DTARGET_DEVICE_NAME="${TARGET_DEVICE}") if(TARGET_DEVICE STREQUAL "PINETIME") add_definitions(-DDRIVER_PINMAP_PINETIME) add_definitions(-DCLOCK_CONFIG_LF_SRC=1) # XTAL + add_definitions(-DDRIVER_TOUCH_DYNAMIC) elseif(TARGET_DEVICE STREQUAL "MOY_TFK5") # P8a add_definitions(-DDRIVER_PINMAP_P8) add_definitions(-DCLOCK_CONFIG_LF_SRC=1) # XTAL + add_definitions(-DDRIVER_TOUCH_GESTURE) elseif(TARGET_DEVICE STREQUAL "MOY_TIN5") # P8a variant 2 add_definitions(-DDRIVER_PINMAP_P8) add_definitions(-DCLOCK_CONFIG_LF_SRC=1) # XTAL + add_definitions(-DDRIVER_TOUCH_GESTURE) elseif(TARGET_DEVICE STREQUAL "MOY_TON5") # P8b add_definitions(-DDRIVER_PINMAP_P8) add_definitions(-DCLOCK_CONFIG_LF_SRC=0) # RC add_definitions(-DMYNEWT_VAL_BLE_LL_SCA=500) add_definitions(-DCLOCK_CONFIG_LF_CAL_ENABLED=1) + add_definitions(-DDRIVER_TOUCH_REPORT) elseif(TARGET_DEVICE STREQUAL "MOY_UNK") # P8b mirrored add_definitions(-DDRIVER_PINMAP_P8) add_definitions(-DCLOCK_CONFIG_LF_SRC=0) # RC add_definitions(-DMYNEWT_VAL_BLE_LL_SCA=500) add_definitions(-DCLOCK_CONFIG_LF_CAL_ENABLED=1) add_definitions(-DDRIVER_DISPLAY_MIRROR) + add_definitions(-DDRIVER_TOUCH_REPORT) else() message(FATAL_ERROR "Invalid TARGET_DEVICE") endif() diff --git a/src/drivers/Cst816s.cpp b/src/drivers/Cst816s.cpp index cf10c895f5..4fba71d135 100644 --- a/src/drivers/Cst816s.cpp +++ b/src/drivers/Cst816s.cpp @@ -1,108 +1,103 @@ #include "drivers/Cst816s.h" +#include "drivers/PinMap.h" #include #include #include #include -#include "drivers/PinMap.h" using namespace Pinetime::Drivers; -/* References : +/* + * References : * This implementation is based on this article : * https://medium.com/@ly.lee/building-a-rust-driver-for-pinetimes-touch-controller-cbc1a5d5d3e9 Touch panel datasheet (weird chinese * translation) : https://wiki.pine64.org/images/5/51/CST816S%E6%95%B0%E6%8D%AE%E6%89%8B%E5%86%8CV1.1.en.pdf * - * TODO : we need a complete datasheet and protocol reference! + * TODO: We need a complete datasheet and protocol reference! + * For register desciptions, see Cst816s_registers.h. Information was collected from various chinese datasheets and documents. * */ Cst816S::Cst816S(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} { } bool Cst816S::Init() { + // Reset the touch driver nrf_gpio_cfg_output(PinMap::Cst816sReset); nrf_gpio_pin_clear(PinMap::Cst816sReset); - vTaskDelay(5); + vTaskDelay(10); nrf_gpio_pin_set(PinMap::Cst816sReset); vTaskDelay(50); - // Wake the touchpanel up - uint8_t dummy; - twiMaster.Read(twiAddress, 0x15, &dummy, 1); - vTaskDelay(5); - twiMaster.Read(twiAddress, 0xa7, &dummy, 1); - vTaskDelay(5); - - // TODO This function check that the device IDs from the controller are equal to the ones - // we expect. However, it seems to return false positive (probably in case of communication issue). - // Also, it seems that some users have pinetimes that works correctly but that report different device IDs - // Until we know more about this, we'll just read the IDs but not take any action in case they are not 'valid' - CheckDeviceIds(); - - /* - [2] EnConLR - Continuous operation can slide around - [1] EnConUD - Slide up and down to enable continuous operation - [0] EnDClick - Enable Double-click action - */ - static constexpr uint8_t motionMask = 0b00000101; - twiMaster.Write(twiAddress, 0xEC, &motionMask, 1); - - /* - [7] EnTest - Interrupt pin to test, enable automatic periodic issued after a low pulse. - [6] EnTouch - When a touch is detected, a periodic pulsed Low. - [5] EnChange - Upon detecting a touch state changes, pulsed Low. - [4] EnMotion - When the detected gesture is pulsed Low. - [0] OnceWLP - Press gesture only issue a pulse signal is low. - */ - static constexpr uint8_t irqCtl = 0b01110000; - twiMaster.Write(twiAddress, 0xFA, &irqCtl, 1); + // Chip ID is suspected to be dependent on embedded firmware and factory configuration, + // and not (just) on hardware type and revision. + if (twiMaster.Read(twiAddress, CHIP_ID, &chipId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { + chipId = 0xFF; + } + // Vendor / project ID and firmware version can vary between devices. + if (twiMaster.Read(twiAddress, PROJ_ID, &vendorId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { + vendorId = 0xFF; + } + if (twiMaster.Read(twiAddress, FW_VERSION, &fwVersion, 1) == TwiMaster::ErrorCodes::TransactionFailed) { + fwVersion = 0xFF; + } + + // These configuration settings will be ignored by chips which were + // fused / pre-configured in the factory (GESTURE and REPORT settings). + // This mainly applies to CST716, however there may be CST816S with static configurations as well. + // The other, freely configureable ones (DYNAMIC), are configured in reporting mode here. + + // Configure motion behaviour + static constexpr uint8_t motionMask = MOTION_MASK_EN_DCLICK | MOTION_MASK_EN_CON_UD | MOTION_MASK_EN_CON_LR; + twiMaster.Write(twiAddress, MOTION_MASK, &motionMask, 1); + + // Configure interrupt generating events + static constexpr uint8_t irqCtl = IRQ_CTL_EN_MOTION | IRQ_CTL_EN_CHANGE | IRQ_CTL_EN_TOUCH; + twiMaster.Write(twiAddress, IRQ_CTL, &irqCtl, 1); return true; } Cst816S::TouchInfos Cst816S::GetTouchInfo() { - Cst816S::TouchInfos info; - uint8_t touchData[7]; - - auto ret = twiMaster.Read(twiAddress, 0, touchData, sizeof(touchData)); - if (ret != TwiMaster::ErrorCodes::NoError) { - info.isValid = false; - return info; + // Some chips fail to wake up even though the reset pin has been toggled. + // They only provide an I2C communication window after a touch interrupt, + // so the first touch interrupt is used to force initialisation. + if (firstEvent) { + Init(); + firstEvent = false; + // The data registers should now be reset, so this touch event will be detected as invalid. } - // This can only be 0 or 1 - uint8_t nbTouchPoints = touchData[touchPointNumIndex] & 0x0f; - uint8_t xHigh = touchData[touchXHighIndex] & 0x0f; - uint8_t xLow = touchData[touchXLowIndex]; - uint16_t x = (xHigh << 8) | xLow; - uint8_t yHigh = touchData[touchYHighIndex] & 0x0f; - uint8_t yLow = touchData[touchYLowIndex]; - uint16_t y = (yHigh << 8) | yLow; - Gestures gesture = static_cast(touchData[gestureIndex]); - - // Validity check - if (x >= maxX || y >= maxY || - (gesture != Gestures::None && gesture != Gestures::SlideDown && gesture != Gestures::SlideUp && gesture != Gestures::SlideLeft && - gesture != Gestures::SlideRight && gesture != Gestures::SingleTap && gesture != Gestures::DoubleTap && - gesture != Gestures::LongPress)) { - info.isValid = false; + // Read gesture metadata and first touch point coordinate block + Cst816S::TouchInfos info; + uint8_t touchData[P1_Y_POS_L + 1]; + auto ret = twiMaster.Read(twiAddress, 0x00, touchData, sizeof(touchData)); + if (ret != TwiMaster::ErrorCodes::NoError) return info; - } - info.x = x; - info.y = y; - info.touching = (nbTouchPoints > 0); - info.gesture = gesture; - info.isValid = true; + // Assemble 12 bit point coordinates from lower 8 bits and higher 4 bits + info.x = ((touchData[P1_X_POS_H] & POS_H_POS_MASK) << 8) | touchData[P1_X_POS_L]; + info.y = ((touchData[P1_Y_POS_H] & POS_H_POS_MASK) << 8) | touchData[P1_Y_POS_L]; + // Evaluate number of touch points + info.touching = (touchData[TD_STATUS] & TD_STATUS_MASK) > 0; + // Decode gesture ID + info.gesture = static_cast(touchData[GESTURE_ID]); + + // Validity check, verify value ranges + info.isValid = (info.x < maxX && info.y < maxY && + (info.gesture == Gestures::None || info.gesture == Gestures::SlideDown || info.gesture == Gestures::SlideUp || + info.gesture == Gestures::SlideLeft || info.gesture == Gestures::SlideRight || info.gesture == Gestures::SingleTap || + info.gesture == Gestures::DoubleTap || info.gesture == Gestures::LongPress)); + return info; } void Cst816S::Sleep() { - nrf_gpio_pin_clear(PinMap::Cst816sReset); - vTaskDelay(5); - nrf_gpio_pin_set(PinMap::Cst816sReset); - vTaskDelay(50); - static constexpr uint8_t sleepValue = 0x03; - twiMaster.Write(twiAddress, 0xA5, &sleepValue, 1); + // This only controls the CST716, the CST816S will ignore this register. + // The CST816S power state is managed using auto-sleep. + + static constexpr uint8_t sleepValue = PWR_MODE_DEEP_SLEEP; + twiMaster.Write(twiAddress, PWR_MODE_CST716, &sleepValue, 1); + NRF_LOG_INFO("[TOUCHPANEL] Sleep"); } @@ -110,21 +105,3 @@ void Cst816S::Wakeup() { Init(); NRF_LOG_INFO("[TOUCHPANEL] Wakeup"); } - -bool Cst816S::CheckDeviceIds() { - // There's mixed information about which register contains which information - if (twiMaster.Read(twiAddress, 0xA7, &chipId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { - chipId = 0xFF; - return false; - } - if (twiMaster.Read(twiAddress, 0xA8, &vendorId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { - vendorId = 0xFF; - return false; - } - if (twiMaster.Read(twiAddress, 0xA9, &fwVersion, 1) == TwiMaster::ErrorCodes::TransactionFailed) { - fwVersion = 0xFF; - return false; - } - - return chipId == 0xb4 && vendorId == 0 && fwVersion == 1; -} diff --git a/src/drivers/Cst816s.h b/src/drivers/Cst816s.h index c50bb73372..9185dc8d89 100644 --- a/src/drivers/Cst816s.h +++ b/src/drivers/Cst816s.h @@ -1,5 +1,6 @@ #pragma once +#include "drivers/Cst816s_registers.h" #include "drivers/TwiMaster.h" namespace Pinetime { @@ -7,14 +8,15 @@ namespace Pinetime { class Cst816S { public: enum class Gestures : uint8_t { - None = 0x00, - SlideDown = 0x01, - SlideUp = 0x02, - SlideLeft = 0x03, - SlideRight = 0x04, - SingleTap = 0x05, - DoubleTap = 0x0B, - LongPress = 0x0C + None = GESTURE_ID_NONE, + SlideDown = GESTURE_ID_SLIDE_DOWN, + SlideUp = GESTURE_ID_SLIDE_UP, + SlideLeft = GESTURE_ID_SLIDE_LEFT, + SlideRight = GESTURE_ID_SLIDE_RIGHT, + SingleTap = GESTURE_ID_SINGLE_TAP, + DoubleTap = GESTURE_ID_DOUBLE_TAP, + LongPress = GESTURE_ID_LONG_PRESS, + Invalid = 0xFF }; struct TouchInfos { @@ -49,21 +51,6 @@ namespace Pinetime { } private: - bool CheckDeviceIds(); - - // Unused/Unavailable commented out - static constexpr uint8_t gestureIndex = 1; - static constexpr uint8_t touchPointNumIndex = 2; - // static constexpr uint8_t touchEventIndex = 3; - static constexpr uint8_t touchXHighIndex = 3; - static constexpr uint8_t touchXLowIndex = 4; - // static constexpr uint8_t touchIdIndex = 5; - static constexpr uint8_t touchYHighIndex = 5; - static constexpr uint8_t touchYLowIndex = 6; - // static constexpr uint8_t touchStep = 6; - // static constexpr uint8_t touchXYIndex = 7; - // static constexpr uint8_t touchMiscIndex = 8; - static constexpr uint8_t maxX = 240; static constexpr uint8_t maxY = 240; @@ -73,6 +60,8 @@ namespace Pinetime { uint8_t chipId; uint8_t vendorId; uint8_t fwVersion; + + bool firstEvent = true; }; } diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index b245e39227..ac6e009989 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -327,7 +327,13 @@ void SystemTask::Work() { // Double Tap needs the touch screen to be in normal mode if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) { - touchPanel.Sleep(); +// REPORT and GESTURE mode sensors must be normal mode for single tap as well +#if !defined(DRIVER_TOUCH_DYNAMIC) + if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap)) +#endif + { + touchPanel.Sleep(); + } } state = SystemTaskState::Sleeping; diff --git a/src/touchhandler/TouchHandler.cpp b/src/touchhandler/TouchHandler.cpp index b29f951fcb..4d9e7e314d 100644 --- a/src/touchhandler/TouchHandler.cpp +++ b/src/touchhandler/TouchHandler.cpp @@ -37,8 +37,34 @@ bool TouchHandler::ProcessTouchInfo(Drivers::Cst816S::TouchInfos info) { if (!info.isValid) { return false; } + + // REPORT configurations (P8b variants) of the fused (usually) Cst716 + // generate multiple "none" gesture events with info.touching == true during the physical gesture. + // The last event is a e.g. "slide" event with info.touching == true. + // gestureReleased state does not have to be computed manually, instead it occurs when event != "none". - // Only a single gesture per touch + // GESTURE configurations (P8a variants) of the fused (usually) Cst716 generate no events during the physical gesture. + // The only event is a e.g. "slide" event with info.touching == true. + // gestureReleased state does not have to be computed manually, instead it occurs everytime. + + // DYNAMIC configurations (PineTime) are configured in reporting mode during initialisation. + // Usually based on the Cst816s, they generate multiple e.g. "slide" gesture events with info.touching == true during the physical + // gesture. The last of these e.g. "slide" events has info.touching == false. gestureReleased state is computed manually by checking for + // the transition to info.touching == false. + + // Unfortunately, there is no way to reliably obtain which configuration is used at runtime. + // In all cases, the event is bubbled up once the gesture is released. + +#if defined(DRIVER_TOUCH_REPORT) + if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) { + gesture = ConvertGesture(info.gesture); + info.touching = false; + } +#elif defined(DRIVER_TOUCH_GESTURE) + if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) { + gesture = ConvertGesture(info.gesture); + } +#elif defined(DRIVER_TOUCH_DYNAMIC) if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) { if (gestureReleased) { if (info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideDown || @@ -59,8 +85,30 @@ bool TouchHandler::ProcessTouchInfo(Drivers::Cst816S::TouchInfos info) { if (!info.touching) { gestureReleased = true; } +#endif currentTouchPoint = {info.x, info.y, info.touching}; return true; } + +void TouchHandler::UpdateLvglTouchPoint() { + if (info.touching) { +#if defined(DRIVER_TOUCH_GESTURE) + // GESTURE config only generates a single event / state change + // so the LVGL wrapper is used to generate a successive release state update + lvgl.SetNewTap(info.x, info.y); +#else + if (!isCancelled) { + lvgl.SetNewTouchPoint(info.x, info.y, true); + } +#endif + } else { + if (isCancelled) { + lvgl.SetNewTouchPoint(-1, -1, false); + isCancelled = false; + } else { + lvgl.SetNewTouchPoint(info.x, info.y, false); + } + } +} From c8acd6e4b36588e24e29941c43d9a7e24442edf5 Mon Sep 17 00:00:00 2001 From: Christoph Honal Date: Sun, 29 Jan 2023 15:44:32 +0100 Subject: [PATCH 4/4] Touch: Fix tap detection for P8b variant --- src/touchhandler/TouchHandler.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/touchhandler/TouchHandler.cpp b/src/touchhandler/TouchHandler.cpp index 4d9e7e314d..10765e8501 100644 --- a/src/touchhandler/TouchHandler.cpp +++ b/src/touchhandler/TouchHandler.cpp @@ -63,6 +63,12 @@ bool TouchHandler::ProcessTouchInfo(Drivers::Cst816S::TouchInfos info) { #elif defined(DRIVER_TOUCH_GESTURE) if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) { gesture = ConvertGesture(info.gesture); + // A new variant configuration behaves in a way such that it generates a gesture event at the start of a physical gesture, + // but does not set the info.touching flag at all. Since gestures are handled separately, special behaviour is only needed + // for the tap event. For the original P8b, which always sets info.touching = true, this operation is idempotent. + if (gesture == TouchEvents::Tap) { + info.touching = true; + } } #elif defined(DRIVER_TOUCH_DYNAMIC) if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) {