Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -794,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()
Expand Down
12 changes: 12 additions & 0 deletions src/displayapp/LittleVgl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 2 additions & 0 deletions src/displayapp/LittleVgl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,6 +66,7 @@ namespace Pinetime {
lv_point_t touchPoint = {};
bool tapped = false;
bool isCancelled = false;
bool simulate_tap_release = false;
};
}
}
151 changes: 64 additions & 87 deletions src/drivers/Cst816s.cpp
Original file line number Diff line number Diff line change
@@ -1,130 +1,107 @@
#include "drivers/Cst816s.h"
#include "drivers/PinMap.h"
#include <FreeRTOS.h>
#include <legacy/nrf_drv_gpiote.h>
#include <nrfx_log.h>
#include <task.h>
#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<Gestures>(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<Gestures>(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");
}

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;
}
35 changes: 12 additions & 23 deletions src/drivers/Cst816s.h
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
#pragma once

#include "drivers/Cst816s_registers.h"
#include "drivers/TwiMaster.h"

namespace Pinetime {
namespace Drivers {
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 {
Expand Down Expand Up @@ -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;

Expand All @@ -73,6 +60,8 @@ namespace Pinetime {
uint8_t chipId;
uint8_t vendorId;
uint8_t fwVersion;

bool firstEvent = true;
};

}
Expand Down
Loading