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