-
-
Notifications
You must be signed in to change notification settings - Fork 959
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
6f1b502
commit 48123d9
Showing
5 changed files
with
123 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters