ESPHome  2023.11.6
xpt2046.cpp
Go to the documentation of this file.
1 #include "xpt2046.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 
5 #include <algorithm>
6 #include <cinttypes>
7 
8 namespace esphome {
9 namespace xpt2046 {
10 
11 static const char *const TAG = "xpt2046";
12 
14 
16  if (this->irq_pin_ != nullptr) {
17  // The pin reports a touch with a falling edge. Unfortunately the pin goes also changes state
18  // while the channels are read and wiring it as an interrupt is not straightforward and would
19  // need careful masking. A GPIO poll is cheap so we'll just use that.
20 
21  this->irq_pin_->setup(); // INPUT
22  this->irq_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
23  this->irq_pin_->setup();
24  this->irq_pin_->attach_interrupt(XPT2046TouchscreenStore::gpio_intr, &this->store_, gpio::INTERRUPT_FALLING_EDGE);
25  }
26  spi_setup();
27  read_adc_(0xD0); // ADC powerdown, enable PENIRQ pin
28 }
29 
31  if ((this->irq_pin_ != nullptr) && (this->store_.touch || this->touched)) {
32  this->store_.touch = false;
33  check_touch_();
34  }
35 }
36 
38  if (this->irq_pin_ == nullptr)
39  check_touch_();
40 }
41 
43  int16_t data[6];
44  bool touch = false;
45  uint32_t now = millis();
46 
47  enable();
48 
49  int16_t touch_pressure_1 = read_adc_(0xB1 /* touch_pressure_1 */);
50  int16_t touch_pressure_2 = read_adc_(0xC1 /* touch_pressure_2 */);
51 
52  this->z_raw = touch_pressure_1 + 0Xfff - touch_pressure_2;
53 
54  touch = (this->z_raw >= this->threshold_);
55  if (touch) {
56  read_adc_(0xD1 /* X */); // dummy Y measure, 1st is always noisy
57  data[0] = read_adc_(0x91 /* Y */);
58  data[1] = read_adc_(0xD1 /* X */); // make 3 x-y measurements
59  data[2] = read_adc_(0x91 /* Y */);
60  data[3] = read_adc_(0xD1 /* X */);
61  data[4] = read_adc_(0x91 /* Y */);
62  }
63 
64  data[5] = read_adc_(0xD0 /* X */); // Last X touch power down
65 
66  disable();
67 
68  if (touch) {
69  this->x_raw = best_two_avg(data[1], data[3], data[5]);
70  this->y_raw = best_two_avg(data[0], data[2], data[4]);
71 
72  ESP_LOGVV(TAG, "Update [x, y] = [%d, %d], z = %d", this->x_raw, this->y_raw, this->z_raw);
73 
74  TouchPoint touchpoint;
75 
76  touchpoint.x = normalize(this->x_raw, this->x_raw_min_, this->x_raw_max_);
77  touchpoint.y = normalize(this->y_raw, this->y_raw_min_, this->y_raw_max_);
78 
79  if (this->swap_x_y_) {
80  std::swap(touchpoint.x, touchpoint.y);
81  }
82 
83  if (this->invert_x_) {
84  touchpoint.x = 0xfff - touchpoint.x;
85  }
86 
87  if (this->invert_y_) {
88  touchpoint.y = 0xfff - touchpoint.y;
89  }
90 
91  switch (static_cast<TouchRotation>(this->display_->get_rotation())) {
92  case ROTATE_0_DEGREES:
93  break;
94  case ROTATE_90_DEGREES:
95  std::swap(touchpoint.x, touchpoint.y);
96  touchpoint.y = 0xfff - touchpoint.y;
97  break;
98  case ROTATE_180_DEGREES:
99  touchpoint.x = 0xfff - touchpoint.x;
100  touchpoint.y = 0xfff - touchpoint.y;
101  break;
102  case ROTATE_270_DEGREES:
103  std::swap(touchpoint.x, touchpoint.y);
104  touchpoint.x = 0xfff - touchpoint.x;
105  break;
106  }
107 
108  touchpoint.x = (int16_t) ((int) touchpoint.x * this->display_->get_width() / 0xfff);
109  touchpoint.y = (int16_t) ((int) touchpoint.y * this->display_->get_height() / 0xfff);
110 
111  if (!this->touched || (now - this->last_pos_ms_) >= this->report_millis_) {
112  ESP_LOGV(TAG, "Touching at [%03X, %03X] => [%3d, %3d]", this->x_raw, this->y_raw, touchpoint.x, touchpoint.y);
113 
114  this->defer([this, touchpoint]() { this->send_touch_(touchpoint); });
115 
116  this->x = touchpoint.x;
117  this->y = touchpoint.y;
118  this->touched = true;
119  this->last_pos_ms_ = now;
120  }
121  }
122 
123  if (!touch && this->touched) {
124  this->x_raw = this->y_raw = this->z_raw = 0;
125  ESP_LOGV(TAG, "Released [%d, %d]", this->x, this->y);
126  this->touched = false;
127  for (auto *listener : this->touch_listeners_)
128  listener->release();
129  }
130 }
131 
132 void XPT2046Component::set_calibration(int16_t x_min, int16_t x_max, int16_t y_min, int16_t y_max) { // NOLINT
133  this->x_raw_min_ = std::min(x_min, x_max);
134  this->x_raw_max_ = std::max(x_min, x_max);
135  this->y_raw_min_ = std::min(y_min, y_max);
136  this->y_raw_max_ = std::max(y_min, y_max);
137  this->invert_x_ = (x_min > x_max);
138  this->invert_y_ = (y_min > y_max);
139 }
140 
142  ESP_LOGCONFIG(TAG, "XPT2046:");
143 
144  LOG_PIN(" IRQ Pin: ", this->irq_pin_);
145  ESP_LOGCONFIG(TAG, " X min: %d", this->x_raw_min_);
146  ESP_LOGCONFIG(TAG, " X max: %d", this->x_raw_max_);
147  ESP_LOGCONFIG(TAG, " Y min: %d", this->y_raw_min_);
148  ESP_LOGCONFIG(TAG, " Y max: %d", this->y_raw_max_);
149 
150  ESP_LOGCONFIG(TAG, " Swap X/Y: %s", YESNO(this->swap_x_y_));
151  ESP_LOGCONFIG(TAG, " Invert X: %s", YESNO(this->invert_x_));
152  ESP_LOGCONFIG(TAG, " Invert Y: %s", YESNO(this->invert_y_));
153 
154  ESP_LOGCONFIG(TAG, " threshold: %d", this->threshold_);
155  ESP_LOGCONFIG(TAG, " Report interval: %" PRIu32, this->report_millis_);
156 
157  LOG_UPDATE_INTERVAL(this);
158 }
159 
161 
162 int16_t XPT2046Component::best_two_avg(int16_t x, int16_t y, int16_t z) { // NOLINT
163  int16_t da, db, dc; // NOLINT
164  int16_t reta = 0;
165 
166  da = (x > y) ? x - y : y - x;
167  db = (x > z) ? x - z : z - x;
168  dc = (z > y) ? z - y : y - z;
169 
170  if (da <= db && da <= dc) {
171  reta = (x + y) >> 1;
172  } else if (db <= da && db <= dc) {
173  reta = (x + z) >> 1;
174  } else {
175  reta = (y + z) >> 1;
176  }
177 
178  return reta;
179 }
180 
181 int16_t XPT2046Component::normalize(int16_t val, int16_t min_val, int16_t max_val) {
182  int16_t ret;
183 
184  if (val <= min_val) {
185  ret = 0;
186  } else if (val >= max_val) {
187  ret = 0xfff;
188  } else {
189  ret = (int16_t) ((int) 0xfff * (val - min_val) / (max_val - min_val));
190  }
191 
192  return ret;
193 }
194 
195 int16_t XPT2046Component::read_adc_(uint8_t ctrl) { // NOLINT
196  uint8_t data[2];
197 
198  write_byte(ctrl);
199  delay(1);
200  data[0] = read_byte();
201  data[1] = read_byte();
202 
203  return ((data[0] << 8) | data[1]) >> 3;
204 }
205 
206 } // namespace xpt2046
207 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:18
int16_t read_adc_(uint8_t ctrl)
Definition: xpt2046.cpp:195
static int16_t normalize(int16_t val, int16_t min_val, int16_t max_val)
Definition: xpt2046.cpp:181
uint16_t x
Definition: tt21100.cpp:17
mopeka_std_values val[4]
void set_calibration(int16_t x_min, int16_t x_max, int16_t y_min, int16_t y_max)
Set the coordinates for the touch screen edges.
Definition: xpt2046.cpp:132
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
uint16_t y
Definition: tt21100.cpp:18
float get_setup_priority() const override
Definition: xpt2046.cpp:160
void swap(optional< T > &x, optional< T > &y)
Definition: optional.h:210
static int16_t best_two_avg(int16_t x, int16_t y, int16_t z)
Definition: xpt2046.cpp:162
void update() override
Read and process the values from the hardware.
Definition: xpt2046.cpp:37
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
static void gpio_intr(XPT2046TouchscreenStore *store)
Definition: xpt2046.cpp:13
void loop() override
Detect the touch if the irq pin is specified.
Definition: xpt2046.cpp:30
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26