TinyGPU
Loading...
Searching...
No Matches
TouchDriverArduino.h
Go to the documentation of this file.
1#pragma once
2/**
3 * @file TouchDriverArduino.h
4 * @brief Concrete touch-controller drivers (XPT2046, FT6236/FT6206,
5 * CST816S, GT911, and the controller-less TouchDriverBitBang) built on
6 * the Arduino SPIClass/TwoWire/pinMode/analogRead API - split out of
7 * TouchDriver.h so that including the platform-independent TouchDriver
8 * base class/calibration/coordinate-mapping code doesn't also pull in
9 * Arduino.h. Include this file explicitly to use any of these drivers.
10 *
11 * Pulls in Arduino.h/SPI.h/Wire.h via TinyGPU/Emulation.h, which falls
12 * back to a small platform-native emulation of that surface when no real
13 * Arduino core (e.g. arduino-esp32) is present - see that header for
14 * what is and isn't covered.
15 */
16
17#include "TinyGPU/Emulation.h"
18#include "TinyGPU/Input/TouchDriverCommon.h"
19
20namespace tinygpu {
21
22/**
23 * @brief XPT2046 resistive touch controller.
24 *
25 * Uses SPI.
26 *
27 * XPT2046 command mapping:
28 * 0x90 = Y position
29 * 0xD0 = X position
30 * 0xB1 = Z1
31 * 0xC1 = Z2
32 */
34 public:
35 TouchDriverXPT2046(SPIClass& spi, int8_t csPin, int8_t irqPin = -1,
36 uint16_t zThreshold = 400)
37 : spi_(spi), csPin_(csPin), irqPin_(irqPin), zThreshold_(zThreshold) {}
38
39 bool begin() override {
40 if (csPin_ < 0) {
41 return false;
42 }
43
44 pinMode(csPin_, OUTPUT);
45 digitalWrite(csPin_, HIGH);
46
47 if (irqPin_ >= 0) {
48 pinMode(irqPin_, INPUT_PULLUP);
49 }
50
51 return true;
52 }
53
54 bool isTouched() override {
55 if (irqPin_ >= 0) {
56 return digitalRead(irqPin_) == LOW;
57 }
58
59 /*
60 * Without PENIRQ we need to perform an actual controller measurement.
61 */
62 Point point;
63 return getPoint(point);
64 }
65
66 bool getPoint(Point& outPoint) override {
67 spi_.beginTransaction(SPISettings(kSpiClock, MSBFIRST, SPI_MODE0));
68
69 digitalWrite(csPin_, LOW);
70
71 /*
72 * Read Z1 and Z2 first to determine whether the panel is pressed.
73 */
74 const uint16_t z1 = transfer12(0xB1);
75 const uint16_t z2 = transfer12(0xC1);
76
77 /*
78 * XPT2046's commonly used touch-presence metric:
79 *
80 * Z = Z1 + 4095 - Z2
81 *
82 * This is useful for thresholding but is NOT a calibrated physical
83 * pressure value in ohms or grams.
84 */
85 const int32_t z =
86 static_cast<int32_t>(z1) + 4095 - static_cast<int32_t>(z2);
87
88 if (z < static_cast<int32_t>(zThreshold_)) {
89 endTransaction();
90 return false;
91 }
92
93 /*
94 * IMPORTANT:
95 *
96 * 0xD0 = X
97 * 0x90 = Y
98 *
99 * The previous implementation had these reversed.
100 *
101 * Discard the first conversion for each axis. This improves stability
102 * on some XPT2046/panel combinations.
103 */
104 (void)transfer12(0xD0);
105 const uint16_t rawX = transfer12(0xD0);
106
107 (void)transfer12(0x90);
108 const uint16_t rawY = transfer12(0x90);
109
110 endTransaction();
111
112 /*
113 * Clamp Z to the representable 12-bit range before exposing it.
114 */
115 const uint16_t pressure = static_cast<uint16_t>(clamp32(z, 0, 4095));
116
117 outPoint = mapCoordinates(static_cast<int16_t>(rawX),
118 static_cast<int16_t>(rawY), pressure);
119
120 return true;
121 }
122
123 void setZThreshold(uint16_t threshold) { zThreshold_ = threshold; }
124
125 uint16_t getZThreshold() const { return zThreshold_; }
126
127 private:
128 static constexpr uint32_t kSpiClock = 2000000UL;
129
130 SPIClass& spi_;
131 int8_t csPin_;
132 int8_t irqPin_;
133 uint16_t zThreshold_;
134
135 uint16_t transfer12(uint8_t command) {
136 spi_.transfer(command);
137
138 const uint8_t high = spi_.transfer(0x00);
139
140 const uint8_t low = spi_.transfer(0x00);
141
142 return static_cast<uint16_t>(
143 ((static_cast<uint16_t>(high) << 8) | static_cast<uint16_t>(low)) >> 3);
144 }
145
146 void endTransaction() {
147 digitalWrite(csPin_, HIGH);
148 spi_.endTransaction();
149 }
150};
151
152/**
153 * @brief Bit-banged 4-wire resistive touch panel driver.
154 *
155 * Drives the resistive layer's four pins (X-, Y-, X+, Y+) directly via
156 * GPIO - pinMode()/digitalWrite()/digitalRead()/analogRead() - rather than
157 * through a dedicated touch controller chip such as XPT2046. This matches
158 * small resistive touch panels that expose their four resistive-layer
159 * pins straight to microcontroller GPIOs instead of routing them through
160 * a controller IC.
161 *
162 * Requires analogRead() to be available for all four pins, i.e. a real
163 * Arduino core (arduino-esp32, AVR, ...) - the ESP-IDF-native
164 * TinyGPU/Emulation fallback (see EmulationIDF.h) does not emulate
165 * analogRead()/ADC, so this driver will not compile against that
166 * fallback.
167 *
168 * Position measurement, per axis:
169 * - drive one axis's plates (e.g. X+ HIGH / X- LOW) to set up a voltage
170 * gradient across that resistive layer
171 * - float the OTHER axis's plates (INPUT)
172 * - analogRead() one of the other axis's pins: the voltage it picks up
173 * is proportional to position along the driven axis
174 *
175 * Touch presence is a cheap digital check (Y+ as INPUT_PULLUP, X- driven
176 * LOW; a touch shorts the two layers together and pulls Y+ LOW) rather
177 * than a calibrated Z1/Z2 pressure measurement - this only needs to know
178 * whether the panel is touched at all. Pressure is therefore always
179 * reported as 255 while touched, like the capacitive controllers below.
180 */
182 public:
183 /**
184 * @param xPlusPin X+ resistive layer analog pin.
185 * @param xMinusPin X- resistive layer digital pin.
186 * @param yPlusPin Y+ resistive layer analog pin.
187 * @param yMinusPin Y- resistive layer digital pin.
188 */
189 TouchDriverBitBang(int8_t xPlusPin, int8_t xMinusPin, int8_t yPlusPin,
190 int8_t yMinusPin)
191 : xp_(xPlusPin), xm_(xMinusPin), yp_(yPlusPin), ym_(yMinusPin) {}
192
193 bool begin() override {
194 if (xp_ < 0 || xm_ < 0 || yp_ < 0 || ym_ < 0) {
195 return false;
196 }
197
198 releasePins();
199 return true;
200 }
201
202 bool isTouched() override {
203 /*
204 * Cheap digital touch-presence check: pull Y+ up internally and drive
205 * X- low. Untouched, Y+'s internal pull-up holds it HIGH. A touch
206 * connects the two resistive layers together, so current flows from
207 * the pulled-up Y+ pin down through X-, pulling Y+ LOW.
208 */
209 pinMode(xp_, INPUT);
210 pinMode(ym_, INPUT);
211
212 pinMode(xm_, OUTPUT);
213 digitalWrite(xm_, LOW);
214
215 pinMode(yp_, INPUT_PULLUP);
216
217 const bool touched = digitalRead(yp_) == LOW;
218
219 releasePins();
220 return touched;
221 }
222
223 bool getPoint(Point& outPoint) override {
224 if (!isTouched()) {
225 return false;
226 }
227
228 const int16_t rawX = readAxis(/*driveXAxis=*/true);
229 const int16_t rawY = readAxis(/*driveXAxis=*/false);
230
231 releasePins();
232
233 outPoint = mapCoordinates(rawX, rawY, 255);
234 return true;
235 }
236
237 private:
238 int8_t xp_;
239 int8_t xm_;
240 int8_t yp_;
241 int8_t ym_;
242
243 void releasePins() {
244 pinMode(xp_, INPUT);
245 pinMode(xm_, INPUT);
246 pinMode(yp_, INPUT);
247 pinMode(ym_, INPUT);
248 }
249
250 /**
251 * @brief Read one axis's raw position.
252 *
253 * driveXAxis == true: drive X+/X- (X+ HIGH, X- LOW), read the resulting
254 * voltage gradient off Y+ - this measures the X position.
255 *
256 * driveXAxis == false: drive Y+/Y- (Y+ HIGH, Y- LOW), read off X+ - this
257 * measures the Y position.
258 */
259 int16_t readAxis(bool driveXAxis) {
260 const int8_t drivePlus = driveXAxis ? xp_ : yp_;
261 const int8_t driveMinus = driveXAxis ? xm_ : ym_;
262 const int8_t sensePin = driveXAxis ? yp_ : xp_;
263 const int8_t senseOtherPin = driveXAxis ? ym_ : xm_;
264
265 pinMode(senseOtherPin, INPUT);
266
267 pinMode(drivePlus, OUTPUT);
268 pinMode(driveMinus, OUTPUT);
269 digitalWrite(drivePlus, HIGH);
270 digitalWrite(driveMinus, LOW);
271
272 pinMode(sensePin, INPUT);
273
274 /*
275 * Discard the first conversion. This lets the driven plates settle
276 * and improves stability, the same way TouchDriverXPT2046 discards
277 * its first conversion per axis.
278 */
279 (void)analogRead(sensePin);
280 return static_cast<int16_t>(analogRead(sensePin));
281 }
282};
283
284/**
285 * @brief FT6236 / FT6336 capacitive touch controller.
286 *
287 * I2C address is normally 0x38.
288 *
289 * Register layout used:
290 *
291 * 0x02 TD_STATUS
292 * 0x03 P1_XH
293 * 0x04 P1_XL
294 * 0x05 P1_YH
295 * 0x06 P1_YL
296 */
298 public:
299 static constexpr uint8_t I2C_ADDR = 0x38;
300
301 TouchDriverFT6236(TwoWire& wire = Wire, int8_t rstPin = -1,
302 int8_t irqPin = -1)
303 : wire_(wire), rstPin_(rstPin), irqPin_(irqPin) {}
304
305 bool begin() override {
306 if (rstPin_ >= 0) {
307 pinMode(rstPin_, OUTPUT);
308
309 digitalWrite(rstPin_, LOW);
310 delay(10);
311
312 digitalWrite(rstPin_, HIGH);
313 delay(300);
314 }
315
316 if (irqPin_ >= 0) {
317 pinMode(irqPin_, INPUT_PULLUP);
318 }
319
320 /*
321 * The caller owns Wire.begin().
322 *
323 * We only verify that the device responds.
324 */
325 wire_.beginTransmission(I2C_ADDR);
326
327 return wire_.endTransmission() == 0;
328 }
329
330 bool isTouched() override {
331 if (irqPin_ >= 0 && digitalRead(irqPin_) == HIGH) {
332 return false;
333 }
334
335 uint8_t buffer[5];
336
337 if (!readRegisters(0x02, buffer, sizeof(buffer))) {
338 return false;
339 }
340
341 const uint8_t touches = buffer[0] & 0x0F;
342
343 return touches > 0;
344 }
345
346 bool getPoint(Point& outPoint) override {
347 if (irqPin_ >= 0 && digitalRead(irqPin_) == HIGH) {
348 return false;
349 }
350
351 /*
352 * Read status + P1 coordinates in one transaction:
353 *
354 * 0x02 TD_STATUS
355 * 0x03 P1_XH
356 * 0x04 P1_XL
357 * 0x05 P1_YH
358 * 0x06 P1_YL
359 */
360 uint8_t buffer[5];
361
362 if (!readRegisters(0x02, buffer, sizeof(buffer))) {
363 return false;
364 }
365
366 const uint8_t touches = buffer[0] & 0x0F;
367
368 if (touches == 0) {
369 return false;
370 }
371
372 const uint16_t rawX =
373 (static_cast<uint16_t>(buffer[1] & 0x0F) << 8) | buffer[2];
374
375 const uint16_t rawY =
376 (static_cast<uint16_t>(buffer[3] & 0x0F) << 8) | buffer[4];
377
378 /*
379 * Capacitive controller does not expose resistive pressure in the
380 * same sense as XPT2046. 255 means "touched".
381 */
382 outPoint = mapCoordinates(static_cast<int16_t>(rawX),
383 static_cast<int16_t>(rawY), 255);
384
385 return true;
386 }
387
388 private:
389 TwoWire& wire_;
390 int8_t rstPin_;
391 int8_t irqPin_;
392
393 bool readRegisters(uint8_t startRegister, uint8_t* buffer, size_t length) {
394 if (buffer == nullptr || length == 0) {
395 return false;
396 }
397
398 wire_.beginTransmission(I2C_ADDR);
399 wire_.write(startRegister);
400
401 if (wire_.endTransmission(false) != 0) {
402 return false;
403 }
404
405 const size_t received =
406 wire_.requestFrom(static_cast<int>(I2C_ADDR), static_cast<int>(length));
407
408 if (received != length) {
409 /*
410 * Drain anything the implementation may have left in the RX buffer.
411 */
412 while (wire_.available()) {
413 (void)wire_.read();
414 }
415
416 return false;
417 }
418
419 for (size_t i = 0; i < length; ++i) {
420 if (!wire_.available()) {
421 return false;
422 }
423
424 buffer[i] = static_cast<uint8_t>(wire_.read());
425 }
426
427 return true;
428 }
429};
430
431
432/// The FT6206 is a drop-in replacement for the FT6236, so we can alias it here.
433using TouchDriverFT6206 = TouchDriverFT6236;
434
435
436/**
437 * @brief CST816S capacitive touch controller.
438 *
439 * I2C address:
440 * 0x15
441 *
442 * Register layout:
443 *
444 * 0x01 GestureID
445 * 0x02 FingerNum
446 * 0x03 XposH
447 * 0x04 XposL
448 * 0x05 YposH
449 * 0x06 YposL
450 */
452 public:
453 static constexpr uint8_t I2C_ADDR = 0x15;
454
455 TouchDriverCST816S(TwoWire& wire = Wire, int8_t rstPin = -1,
456 int8_t irqPin = -1)
457 : wire_(wire), rstPin_(rstPin), irqPin_(irqPin) {}
458
459 bool begin() override {
460 if (rstPin_ >= 0) {
461 pinMode(rstPin_, OUTPUT);
462
463 digitalWrite(rstPin_, LOW);
464 delay(20);
465
466 digitalWrite(rstPin_, HIGH);
467 delay(50);
468 }
469
470 if (irqPin_ >= 0) {
471 pinMode(irqPin_, INPUT_PULLUP);
472 }
473
474 /*
475 * The application owns Wire.begin().
476 */
477 wire_.beginTransmission(I2C_ADDR);
478
479 return wire_.endTransmission() == 0;
480 }
481
482 bool isTouched() override {
483 if (irqPin_ >= 0 && digitalRead(irqPin_) == HIGH) {
484 return false;
485 }
486
487 uint8_t fingerNum = 0;
488
489 if (!readRegisters(0x02, &fingerNum, 1)) {
490 return false;
491 }
492
493 return fingerNum != 0;
494 }
495
496 bool getPoint(Point& outPoint) override {
497 if (irqPin_ >= 0 && digitalRead(irqPin_) == HIGH) {
498 return false;
499 }
500
501 /*
502 * Read all relevant touch information in one transaction:
503 *
504 * 0x01 GestureID
505 * 0x02 FingerNum
506 * 0x03 XposH
507 * 0x04 XposL
508 * 0x05 YposH
509 * 0x06 YposL
510 */
511 uint8_t buffer[6];
512
513 if (!readRegisters(0x01, buffer, sizeof(buffer))) {
514 return false;
515 }
516
517 const uint8_t fingerNum = buffer[1];
518
519 if (fingerNum == 0) {
520 return false;
521 }
522
523 const uint16_t rawX =
524 (static_cast<uint16_t>(buffer[2] & 0x0F) << 8) | buffer[3];
525
526 const uint16_t rawY =
527 (static_cast<uint16_t>(buffer[4] & 0x0F) << 8) | buffer[5];
528
529 outPoint = mapCoordinates(static_cast<int16_t>(rawX),
530 static_cast<int16_t>(rawY), 255);
531
532 return true;
533 }
534
535 private:
536 TwoWire& wire_;
537 int8_t rstPin_;
538 int8_t irqPin_;
539
540 bool readRegisters(uint8_t startRegister, uint8_t* buffer, size_t length) {
541 if (buffer == nullptr || length == 0) {
542 return false;
543 }
544
545 wire_.beginTransmission(I2C_ADDR);
546 wire_.write(startRegister);
547
548 /*
549 * Repeated-start is preferable because it keeps the register address
550 * transaction together with the subsequent read.
551 */
552 if (wire_.endTransmission(false) != 0) {
553 return false;
554 }
555
556 const size_t received =
557 wire_.requestFrom(static_cast<int>(I2C_ADDR), static_cast<int>(length));
558
559 if (received != length) {
560 while (wire_.available()) {
561 (void)wire_.read();
562 }
563
564 return false;
565 }
566
567 for (size_t i = 0; i < length; ++i) {
568 if (!wire_.available()) {
569 return false;
570 }
571
572 buffer[i] = static_cast<uint8_t>(wire_.read());
573 }
574
575 return true;
576 }
577};
578
579/**
580 * @brief GT911 capacitive touch controller.
581 *
582 * I2C address is normally 0x5D (alt 0x14, selected via the INT pin's level
583 * while RST is pulsed - see begin()). Unlike FT6236/CST816S, GT911's
584 * register addresses are 16-bit (MSB first), and its X/Y bytes are
585 * little-endian (low byte first) rather than the big-endian nibble-packed
586 * layout FT6236 uses.
587 *
588 * Register layout used:
589 *
590 * 0x8100 CONFIG_FRESH: write 1 after reset to make the chip start its
591 * sensing engine. Without this, the chip stays fully alive and
592 * correctly configured but never scans - status sits at 0x80
593 * ("config loaded, idle") forever.
594 * 0x80FF config checksum: round-tripped (read then written back
595 * unchanged) alongside the CONFIG_FRESH write.
596 * 0x814E status: bit7 = buffer ready, bits[3:0] = touch point count.
597 * Must be cleared (write 0x00) unconditionally on every poll,
598 * not only when a touch was found - clearing it only after a
599 * detected touch is a deadlock, since a stale, unacknowledged
600 * flag prevents the chip from ever reporting a new one.
601 * 0x814F point 0 data (7 bytes: track_id, x_lo, x_hi, y_lo, y_hi,
602 * size_lo, size_hi); point N starts at 0x814F + N*7.
603 *
604 * Because status must be cleared unconditionally exactly once per poll
605 * cycle, isTouched() does the chip's actual read/clear I/O and caches
606 * whatever point data was present; getPoint()/getSecondPoint() just
607 * return the cached result rather than re-querying the chip. Call
608 * isTouched() once per loop iteration (the same usage pattern as this
609 * file's other touch drivers) - calling it more than once between
610 * getPoint() calls will consume/clear a pending touch before getPoint()
611 * sees it.
612 *
613 * GT911 genuinely reports up to 5 simultaneous touches (unlike the
614 * single-touch FT6236/CST816S/XPT2046 parts also in this file), so
615 * getSecondPoint() is a real second contact, not a stub.
616 */
618 public:
619 static constexpr uint8_t I2C_ADDR_1 = 0x5D;
620 static constexpr uint8_t I2C_ADDR_2 = 0x14;
621
622 TouchDriverGT911(TwoWire& wire = Wire, int8_t rstPin = -1,
623 int8_t irqPin = -1, uint8_t i2cAddr = I2C_ADDR_1)
624 : wire_(wire), rstPin_(rstPin), irqPin_(irqPin), addr_(i2cAddr) {}
625
626 bool begin() override {
627 if (rstPin_ >= 0) {
628 /*
629 * Address-select reset sequence (GT911 datasheet): INT is held at
630 * the level matching the desired I2C address while RST is pulsed,
631 * then released to become the (unused, polled) touch IRQ line.
632 */
633 pinMode(rstPin_, OUTPUT);
634
635 if (irqPin_ >= 0) {
636 pinMode(irqPin_, OUTPUT);
637 digitalWrite(irqPin_, addr_ == I2C_ADDR_1 ? LOW : HIGH);
638 }
639
640 digitalWrite(rstPin_, LOW);
641 delay(10);
642
643 if (irqPin_ >= 0) {
644 digitalWrite(irqPin_, addr_ == I2C_ADDR_1 ? LOW : HIGH);
645 }
646 delay(1);
647
648 digitalWrite(rstPin_, HIGH);
649 delay(5);
650
651 if (irqPin_ >= 0) {
652 digitalWrite(irqPin_, LOW);
653 }
654 delay(50);
655 }
656
657 if (irqPin_ >= 0) {
658 pinMode(irqPin_, INPUT_PULLUP);
659 }
660
661 delay(50); // GT911 needs time after reset before it will ACK on I2C
662
663 wire_.beginTransmission(addr_);
664 if (wire_.endTransmission() != 0) {
665 return false;
666 }
667
668 /* Activate the sensing engine - see class comment. */
669 uint8_t checksum = 0;
670 readRegister(kRegConfigChecksum, checksum);
671 writeRegister(kRegConfigChecksum, checksum);
672 writeRegister(kRegConfigFresh, 0x01);
673 delay(10);
674
675 return true;
676 }
677
678 bool isTouched() override {
679 uint8_t status = 0;
680 readRegister(kRegStatus, status);
681 const bool bufferReady = (status & 0x80) != 0;
682 cachedTouches_ = status & 0x0F;
683
684 if (bufferReady && cachedTouches_ > 0) {
685 const uint8_t pointsToRead =
686 cachedTouches_ > 2 ? 2 : cachedTouches_; // this driver only caches 2
687
688 for (uint8_t i = 0; i < pointsToRead; ++i) {
689 uint8_t buf[7];
690 if (readRegisters(kRegPoint0 + i * 7, buf, sizeof(buf))) {
691 const uint16_t rawX = (static_cast<uint16_t>(buf[2]) << 8) | buf[1];
692 const uint16_t rawY = (static_cast<uint16_t>(buf[4]) << 8) | buf[3];
693 cachedPoints_[i] = mapCoordinates(static_cast<int16_t>(rawX),
694 static_cast<int16_t>(rawY), 255);
695 }
696 }
697 }
698
699 writeRegister(kRegStatus, 0x00); // unconditional - see class comment
700 return cachedTouches_ > 0;
701 }
702
703 bool getPoint(Point& outPoint) override {
704 if (cachedTouches_ == 0) return false;
705 outPoint = cachedPoints_[0];
706 return true;
707 }
708
709 bool getSecondPoint(Point& outPoint) override {
710 if (cachedTouches_ < 2) return false;
711 outPoint = cachedPoints_[1];
712 return true;
713 }
714
715 private:
716 static constexpr uint16_t kRegConfigChecksum = 0x80FF;
717 static constexpr uint16_t kRegConfigFresh = 0x8100;
718 static constexpr uint16_t kRegStatus = 0x814E;
719 static constexpr uint16_t kRegPoint0 = 0x814F;
720
721 TwoWire& wire_;
722 int8_t rstPin_;
723 int8_t irqPin_;
724 uint8_t addr_;
725 uint8_t cachedTouches_ = 0;
726 Point cachedPoints_[2];
727
728 bool readRegister(uint16_t reg, uint8_t& value) {
729 return readRegisters(reg, &value, 1);
730 }
731
732 bool readRegisters(uint16_t startRegister, uint8_t* buffer, size_t length) {
733 if (buffer == nullptr || length == 0) {
734 return false;
735 }
736
737 wire_.beginTransmission(addr_);
738 wire_.write(static_cast<uint8_t>(startRegister >> 8));
739 wire_.write(static_cast<uint8_t>(startRegister & 0xFF));
740
741 if (wire_.endTransmission(false) != 0) {
742 return false;
743 }
744
745 const size_t received =
746 wire_.requestFrom(static_cast<int>(addr_), static_cast<int>(length));
747
748 if (received != length) {
749 while (wire_.available()) {
750 (void)wire_.read();
751 }
752 return false;
753 }
754
755 for (size_t i = 0; i < length; ++i) {
756 if (!wire_.available()) {
757 return false;
758 }
759 buffer[i] = static_cast<uint8_t>(wire_.read());
760 }
761
762 return true;
763 }
764
765 void writeRegister(uint16_t reg, uint8_t value) {
766 wire_.beginTransmission(addr_);
767 wire_.write(static_cast<uint8_t>(reg >> 8));
768 wire_.write(static_cast<uint8_t>(reg & 0xFF));
769 wire_.write(value);
770 wire_.endTransmission();
771 }
772};
773
774} // namespace tinygpu
Bit-banged 4-wire resistive touch panel driver.
Definition: TouchDriverArduino.h:181
bool isTouched() override
Return whether the panel currently appears touched.
Definition: TouchDriverArduino.h:202
bool getPoint(Point &outPoint) override
Read the current touch point.
Definition: TouchDriverArduino.h:223
TouchDriverBitBang(int8_t xPlusPin, int8_t xMinusPin, int8_t yPlusPin, int8_t yMinusPin)
Definition: TouchDriverArduino.h:189
bool begin() override
Initialize the controller.
Definition: TouchDriverArduino.h:193
CST816S capacitive touch controller.
Definition: TouchDriverArduino.h:451
bool isTouched() override
Return whether the panel currently appears touched.
Definition: TouchDriverArduino.h:482
bool getPoint(Point &outPoint) override
Read the current touch point.
Definition: TouchDriverArduino.h:496
bool begin() override
Initialize the controller.
Definition: TouchDriverArduino.h:459
TouchDriverCST816S(TwoWire &wire=Wire, int8_t rstPin=-1, int8_t irqPin=-1)
Definition: TouchDriverArduino.h:455
static constexpr uint8_t I2C_ADDR
Definition: TouchDriverArduino.h:453
FT6236 / FT6336 capacitive touch controller.
Definition: TouchDriverArduino.h:297
bool isTouched() override
Return whether the panel currently appears touched.
Definition: TouchDriverArduino.h:330
bool getPoint(Point &outPoint) override
Read the current touch point.
Definition: TouchDriverArduino.h:346
TouchDriverFT6236(TwoWire &wire=Wire, int8_t rstPin=-1, int8_t irqPin=-1)
Definition: TouchDriverArduino.h:301
bool begin() override
Initialize the controller.
Definition: TouchDriverArduino.h:305
static constexpr uint8_t I2C_ADDR
Definition: TouchDriverArduino.h:299
GT911 capacitive touch controller.
Definition: TouchDriverArduino.h:617
bool isTouched() override
Return whether the panel currently appears touched.
Definition: TouchDriverArduino.h:678
static constexpr uint8_t I2C_ADDR_2
Definition: TouchDriverArduino.h:620
bool getPoint(Point &outPoint) override
Read the current touch point.
Definition: TouchDriverArduino.h:703
static constexpr uint8_t I2C_ADDR_1
Definition: TouchDriverArduino.h:619
TouchDriverGT911(TwoWire &wire=Wire, int8_t rstPin=-1, int8_t irqPin=-1, uint8_t i2cAddr=I2C_ADDR_1)
Definition: TouchDriverArduino.h:622
bool begin() override
Initialize the controller.
Definition: TouchDriverArduino.h:626
bool getSecondPoint(Point &outPoint) override
Read a second, simultaneous touch point, for multi-touch gestures (pinch/rotate).
Definition: TouchDriverArduino.h:709
XPT2046 resistive touch controller.
Definition: TouchDriverArduino.h:33
bool isTouched() override
Return whether the panel currently appears touched.
Definition: TouchDriverArduino.h:54
TouchDriverXPT2046(SPIClass &spi, int8_t csPin, int8_t irqPin=-1, uint16_t zThreshold=400)
Definition: TouchDriverArduino.h:35
bool getPoint(Point &outPoint) override
Read the current touch point.
Definition: TouchDriverArduino.h:66
uint16_t getZThreshold() const
Definition: TouchDriverArduino.h:125
bool begin() override
Initialize the controller.
Definition: TouchDriverArduino.h:39
void setZThreshold(uint16_t threshold)
Definition: TouchDriverArduino.h:123
Base class for touch controllers.
Definition: TouchDriverCommon.h:72
Point mapCoordinates(int16_t rawX, int16_t rawY, uint16_t pressure=255) const
Convert raw controller coordinates into logical display space.
Definition: TouchDriverCommon.h:208
static int32_t clamp32(int32_t value, int32_t minimum, int32_t maximum)
Clamp an integer to an inclusive range.
Definition: TouchDriverCommon.h:159
Definition: DSIBusESP32.h:19
void delay(unsigned long ms)
Definition: EmulationDesktop.h:52
Touch point.
Definition: TouchDriverCommon.h:32