|
| 1 | +# Based on cst816s, Copyright (c) 2024 - 2025 Kevin G. Schlosser |
| 2 | + |
| 3 | +from micropython import const # NOQA |
| 4 | +import pointer_framework |
| 5 | +import time |
| 6 | +import machine # NOQA |
| 7 | + |
| 8 | + |
| 9 | +I2C_ADDR = 0x15 |
| 10 | +BITS = 8 |
| 11 | + |
| 12 | +# 0x00: No gesture |
| 13 | +# 0x01: Swipe up |
| 14 | +# 0x02: Swipe down |
| 15 | +# 0x03: Swipe left |
| 16 | +# 0x04: Swipe right |
| 17 | +# 0x05: Single click |
| 18 | +# 0x0B: Double click |
| 19 | +# 0x0C: Long press |
| 20 | +_GestureID = const(0x01) |
| 21 | + |
| 22 | +# 0: No finger |
| 23 | +# 1: 1 finger |
| 24 | +_FingerNum = const(0x02) |
| 25 | + |
| 26 | +# & 0xF << 8 |
| 27 | +_XposH = const(0x03) |
| 28 | +_XposL = const(0x04) |
| 29 | + |
| 30 | +# & 0xF << 8 |
| 31 | +_YposH = const(0x05) |
| 32 | +_YposL = const(0x06) |
| 33 | + |
| 34 | +_RegisterVersion = const(0x15) |
| 35 | + |
| 36 | +_BPC0H = const(0xB0) |
| 37 | +_BPC0L = const(0xB1) |
| 38 | + |
| 39 | +_BPC1H = const(0xB2) |
| 40 | +_BPC1L = const(0xB3) |
| 41 | + |
| 42 | +_ChipID = const(0xA7) |
| 43 | +_ChipIDValue = const(0xB7) # 0xB5=cst816s, 0xB7=cst820 |
| 44 | + |
| 45 | +_ProjID = const(0xA8) |
| 46 | +_FwVersion = const(0xA9) |
| 47 | + |
| 48 | + |
| 49 | +# =============================== |
| 50 | +_MotionMask = const(0xEC) |
| 51 | + |
| 52 | +# Enables continuous left and right sliding |
| 53 | +_EnConLR = const(0x04) |
| 54 | +# Enables continuous up and down sliding |
| 55 | +_EnConUD = const(0x02) |
| 56 | +# Enable double-click action |
| 57 | +_EnDClick = const(0x01) |
| 58 | +# =============================== |
| 59 | + |
| 60 | +# Interrupt low pulse output width. |
| 61 | +# Unit 0.1ms, optional value: 1~200. The default value is 10. |
| 62 | +_IrqPluseWidth = const(0xED) |
| 63 | + |
| 64 | +# Normal fast detection cycle. |
| 65 | +# This value affects LpAutoWakeTime and AutoSleepTime. |
| 66 | +# Unit 10ms, optional value: 1~30. The default value is 1. |
| 67 | +_NorScanPer = const(0xEE) |
| 68 | + |
| 69 | +# Gesture detection sliding partition angle control. Angle=tan(c)*10 |
| 70 | +# c is the angle based on the positive direction of the x-axis. |
| 71 | +_MotionSlAngle = const(0xEF) |
| 72 | + |
| 73 | +_LpScanRaw1H = const(0xF0) |
| 74 | +_LpScanRaw1L = const(0xF1) |
| 75 | +_LpScanRaw2H = const(0xF2) |
| 76 | +_LpScanRaw2L = const(0xF3) |
| 77 | + |
| 78 | +# Automatic recalibration period in low power consumption. |
| 79 | +# Unit: 1 minute, optional value: 1 to 5. The default value is 5. |
| 80 | +_LpAutoWakeTime = const(0xF4) |
| 81 | + |
| 82 | + |
| 83 | +# Low power scan wake-up threshold. The smaller the value, |
| 84 | +# the more sensitive it is. |
| 85 | +# Optional values: 1 to 255. The default value is 48. |
| 86 | +_LpScanTH = const(0xF5) |
| 87 | + |
| 88 | +# Low power scan range. The larger the value, the more sensitive it is, |
| 89 | +# and the higher the power consumption is. |
| 90 | +# Optional values: 0, 1, 2, 3. The default value is 3. |
| 91 | +_LpScanWin = const(0xF6) |
| 92 | + |
| 93 | +# Low power scan frequency. The smaller the value, the more sensitive it is. |
| 94 | +# Optional values: 1 to 255. The default value is 7. |
| 95 | +_LpScanFreq = const(0xF7) |
| 96 | + |
| 97 | +# Low power scan current. The smaller the value, the more sensitive it is. |
| 98 | +# Optional values: 1 to 255. |
| 99 | +_LpScanIdac = const(0xF8) |
| 100 | + |
| 101 | + |
| 102 | +# Automatically enters low power mode when there is no touch within x seconds. |
| 103 | +# Unit: 1S, default value: 2S. |
| 104 | +_AutoSleepTime = const(0xF9) |
| 105 | + |
| 106 | +# =============================== |
| 107 | +_IrqCtl = const(0xFA) |
| 108 | +# Interrupt pin test, automatically sends low pulses periodically after enabling |
| 109 | +_EnTest = const(0x80) |
| 110 | +# Sends low pulses periodically when touch is detected. |
| 111 | +_EnTouch = const(0x40) |
| 112 | +# Sends low pulses when touch state changes are detected. |
| 113 | +_EnChange = const(0x20) |
| 114 | +# Sends low pulses when gestures are detected. |
| 115 | +_EnMotion = const(0x10) |
| 116 | +# Long press gesture only sends one low pulse signal. |
| 117 | +_OnceWLP = const(0x01) |
| 118 | +# =============================== |
| 119 | + |
| 120 | + |
| 121 | +# Automatically reset when there is touch but no valid gesture within x seconds. |
| 122 | +# Unit: 1S. This function is not enabled when it is 0. The default value is 5. |
| 123 | +_AutoReset = const(0xFB) |
| 124 | + |
| 125 | +# Automatically reset after long pressing for x seconds. |
| 126 | +# Unit: 1S. This function is not enabled when it is 0. The default value is 10. |
| 127 | +_LongPressTime = const(0xFC) |
| 128 | + |
| 129 | +# =============================== |
| 130 | +_IOCtl = const(0xFD) |
| 131 | + |
| 132 | +# The master controller realizes the soft reset function |
| 133 | +# of the touch screen by pulling down the IRQ pin. |
| 134 | +# 0: Disable soft reset. |
| 135 | +# 1: Enable soft reset. |
| 136 | +_SOFT_RST = const(0x04) |
| 137 | + |
| 138 | +# IIC pin drive mode, the default is resistor pull-up. |
| 139 | +# 0: Resistor pull-up |
| 140 | +# 1: OD |
| 141 | +_IIC_OD = const(0x02) |
| 142 | + |
| 143 | +# IIC and IRQ pin level selection, the default is VDD level. |
| 144 | +# 0: VDD |
| 145 | +# 1: 1.8V |
| 146 | +_En1v8 = const(0x01) |
| 147 | +# =============================== |
| 148 | + |
| 149 | +# The default value is 0, enabling automatic entry into low power mode. |
| 150 | +# When the value is non-zero, automatic entry into low power mode is disabled. |
| 151 | +# 0: enabled |
| 152 | +# 1: disabled |
| 153 | +_DisAutoSleep = const(0xFE) |
| 154 | + |
| 155 | + |
| 156 | +class CST816S(pointer_framework.PointerDriver): |
| 157 | + |
| 158 | + def _read_reg(self, reg): |
| 159 | + self._tx_buf[0] = reg |
| 160 | + self._rx_buf[0] = 0x00 |
| 161 | + |
| 162 | + self._device.write_readinto(self._tx_mv[:1], self._rx_mv[:1]) |
| 163 | + |
| 164 | + def _write_reg(self, reg, value): |
| 165 | + self._tx_buf[0] = reg |
| 166 | + self._tx_buf[1] = value |
| 167 | + self._device.write(self._tx_mv[:2]) |
| 168 | + |
| 169 | + def __init__( |
| 170 | + self, |
| 171 | + device, |
| 172 | + reset_pin=None, |
| 173 | + touch_cal=None, |
| 174 | + startup_rotation=pointer_framework.lv.DISPLAY_ROTATION._0, # NOQA |
| 175 | + debug=False |
| 176 | + ): |
| 177 | + self._tx_buf = bytearray(2) |
| 178 | + self._tx_mv = memoryview(self._tx_buf) |
| 179 | + self._rx_buf = bytearray(1) |
| 180 | + self._rx_mv = memoryview(self._rx_buf) |
| 181 | + |
| 182 | + self._device = device |
| 183 | + |
| 184 | + if not isinstance(reset_pin, int): |
| 185 | + self._reset_pin = reset_pin |
| 186 | + else: |
| 187 | + self._reset_pin = machine.Pin(reset_pin, machine.Pin.OUT) |
| 188 | + |
| 189 | + if self._reset_pin: |
| 190 | + self._reset_pin.value(1) |
| 191 | + |
| 192 | + self.hw_reset() |
| 193 | + self.auto_sleep = False |
| 194 | + |
| 195 | + self._read_reg(_ChipID) |
| 196 | + print('Chip ID:', hex(self._rx_buf[0])) |
| 197 | + chip_id = self._rx_buf[0] |
| 198 | + |
| 199 | + self._read_reg(_RegisterVersion) |
| 200 | + print('Touch version:', self._rx_buf[0]) |
| 201 | + |
| 202 | + self._read_reg(_ProjID) |
| 203 | + print('Proj ID:', hex(self._rx_buf[0])) |
| 204 | + |
| 205 | + self._read_reg(_FwVersion) |
| 206 | + print('FW Version:', hex(self._rx_buf[0])) |
| 207 | + |
| 208 | + if chip_id != _ChipIDValue: |
| 209 | + raise RuntimeError(f'Incorrect chip id ({hex(chip_id)} expected: {hex(_ChipIDValue)})') |
| 210 | + |
| 211 | + self._write_reg(_IrqCtl, _EnTouch | _EnChange) |
| 212 | + |
| 213 | + super().__init__( |
| 214 | + touch_cal=touch_cal, startup_rotation=startup_rotation, debug=debug |
| 215 | + ) |
| 216 | + |
| 217 | + @property |
| 218 | + def wake_up_threshold(self): |
| 219 | + self._read_reg(_LpScanTH) |
| 220 | + return 256 - self._rx_buf[0] |
| 221 | + |
| 222 | + @wake_up_threshold.setter |
| 223 | + def wake_up_threshold(self, value): |
| 224 | + if value < 1: |
| 225 | + value = 1 |
| 226 | + elif value > 255: |
| 227 | + value = 255 |
| 228 | + |
| 229 | + self._write_reg(_LpScanTH, 256 - value) |
| 230 | + |
| 231 | + @property |
| 232 | + def wake_up_scan_frequency(self): |
| 233 | + self._read_reg(_LpScanFreq) |
| 234 | + return 256 - self._rx_buf[0] |
| 235 | + |
| 236 | + @wake_up_scan_frequency.setter |
| 237 | + def wake_up_scan_frequency(self, value): |
| 238 | + if value < 1: |
| 239 | + value = 1 |
| 240 | + elif value > 255: |
| 241 | + value = 255 |
| 242 | + |
| 243 | + self._write_reg(_LpScanFreq, 256 - value) |
| 244 | + |
| 245 | + @property |
| 246 | + def auto_sleep_timeout(self): |
| 247 | + self._read_reg(_AutoSleepTime) |
| 248 | + return self._rx_buf[0] |
| 249 | + |
| 250 | + @auto_sleep_timeout.setter |
| 251 | + def auto_sleep_timeout(self, value): |
| 252 | + if value < 1: |
| 253 | + value = 1 |
| 254 | + elif value > 255: |
| 255 | + value = 255 |
| 256 | + |
| 257 | + self._write_reg(_AutoSleepTime, value) |
| 258 | + |
| 259 | + def wake_up(self): |
| 260 | + auto_sleep = self.auto_sleep |
| 261 | + |
| 262 | + self._write_reg(_DisAutoSleep, 0x00) |
| 263 | + time.sleep_ms(10) # NOQA |
| 264 | + self._write_reg(_DisAutoSleep, 0x01) |
| 265 | + time.sleep_ms(50) # NOQA |
| 266 | + self._write_reg(_DisAutoSleep, 0x01) |
| 267 | + time.sleep_ms(50) # NOQA |
| 268 | + self._write_reg(_DisAutoSleep, int(not auto_sleep)) |
| 269 | + |
| 270 | + @property |
| 271 | + def auto_sleep(self): |
| 272 | + self._read_reg(_DisAutoSleep) |
| 273 | + return self._rx_buf[0] == 0x00 |
| 274 | + |
| 275 | + @auto_sleep.setter |
| 276 | + def auto_sleep(self, en): |
| 277 | + if en: |
| 278 | + self._write_reg(_DisAutoSleep, 0x00) |
| 279 | + else: |
| 280 | + self._write_reg(_DisAutoSleep, 0x01) |
| 281 | + |
| 282 | + def hw_reset(self): |
| 283 | + if self._reset_pin is None: |
| 284 | + return |
| 285 | + |
| 286 | + self._reset_pin(0) |
| 287 | + time.sleep_ms(1) # NOQA |
| 288 | + self._reset_pin(1) |
| 289 | + time.sleep_ms(50) # NOQA |
| 290 | + |
| 291 | + def _get_coords(self): |
| 292 | + self._read_reg(_FingerNum) |
| 293 | + if self._rx_buf[0] == 0: |
| 294 | + return None |
| 295 | + |
| 296 | + self._read_reg(_XposH) |
| 297 | + x = (self._rx_buf[0] & 0x0F) << 8 |
| 298 | + self._read_reg(_XposL) |
| 299 | + x |= self._rx_buf[0] |
| 300 | + |
| 301 | + self._read_reg(_YposH) |
| 302 | + y = (self._rx_buf[0] & 0x0F) << 8 |
| 303 | + self._read_reg(_YposL) |
| 304 | + y |= self._rx_buf[0] |
| 305 | + |
| 306 | + return self.PRESSED, x, y |
0 commit comments