summaryrefslogtreecommitdiff
path: root/src/drivers/Cst816s.cpp
blob: 46dd96dcb23f144d3f94015e1dafd20420ea9699 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include "Cst816s.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 :
 * 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!
 * */

Cst816S::Cst816S(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} {
}

bool Cst816S::Init() {
  nrf_gpio_cfg_output(PinMap::Cst816sReset);
  nrf_gpio_pin_clear(PinMap::Cst816sReset);
  vTaskDelay(5);
  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);

  static constexpr uint8_t maxRetries = 3;
  bool isDeviceOk = false;
  uint8_t retries = 0;
  do {
    isDeviceOk = CheckDeviceIds();
    retries++;
  } while(!isDeviceOk && retries < maxRetries);

  if(!isDeviceOk) {
    return false;
  }

  /*
  [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);

  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;
  }

  // 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;
    return info;
  }

  info.x = x;
  info.y = y;
  info.touching = (nbTouchPoints > 0);
  info.gesture = gesture;
  info.isValid = true;
  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);
  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;
}