ESPHome  1.15.1
esphal.cpp
Go to the documentation of this file.
1 #include "esphome/core/esphal.h"
2 #include "esphome/core/helpers.h"
3 #include "esphome/core/defines.h"
4 #include "esphome/core/log.h"
5 
6 #ifdef ARDUINO_ARCH_ESP8266
7 extern "C" {
8 typedef struct { // NOLINT
9  void *interruptInfo; // NOLINT
10  void *functionInfo; // NOLINT
11 } ArgStructure;
12 
13 void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, void (*)(void *), void *fp, // NOLINT
14  int mode);
15 void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin); // NOLINT
16 };
17 #endif
18 
19 namespace esphome {
20 
21 static const char *TAG = "esphal";
22 
23 GPIOPin::GPIOPin(uint8_t pin, uint8_t mode, bool inverted)
24  : pin_(pin),
25  mode_(mode),
26  inverted_(inverted),
27 #ifdef ARDUINO_ARCH_ESP8266
28  gpio_read_(pin < 16 ? &GPI : &GP16I),
29  gpio_mask_(pin < 16 ? (1UL << pin) : 1)
30 #endif
31 #ifdef ARDUINO_ARCH_ESP32
32  gpio_set_(pin < 32 ? &GPIO.out_w1ts : &GPIO.out1_w1ts.val),
33  gpio_clear_(pin < 32 ? &GPIO.out_w1tc : &GPIO.out1_w1tc.val),
34  gpio_read_(pin < 32 ? &GPIO.in : &GPIO.in1.val),
35  gpio_mask_(pin < 32 ? (1UL << pin) : (1UL << (pin - 32)))
36 #endif
37 {
38 }
39 
40 const char *GPIOPin::get_pin_mode_name() const {
41  const char *mode_s;
42  switch (this->mode_) {
43  case INPUT:
44  mode_s = "INPUT";
45  break;
46  case OUTPUT:
47  mode_s = "OUTPUT";
48  break;
49  case INPUT_PULLUP:
50  mode_s = "INPUT_PULLUP";
51  break;
52  case OUTPUT_OPEN_DRAIN:
53  mode_s = "OUTPUT_OPEN_DRAIN";
54  break;
55  case SPECIAL:
56  mode_s = "SPECIAL";
57  break;
58  case FUNCTION_1:
59  mode_s = "FUNCTION_1";
60  break;
61  case FUNCTION_2:
62  mode_s = "FUNCTION_2";
63  break;
64  case FUNCTION_3:
65  mode_s = "FUNCTION_3";
66  break;
67  case FUNCTION_4:
68  mode_s = "FUNCTION_4";
69  break;
70 
71 #ifdef ARDUINO_ARCH_ESP32
72  case PULLUP:
73  mode_s = "PULLUP";
74  break;
75  case PULLDOWN:
76  mode_s = "PULLDOWN";
77  break;
78  case INPUT_PULLDOWN:
79  mode_s = "INPUT_PULLDOWN";
80  break;
81  case OPEN_DRAIN:
82  mode_s = "OPEN_DRAIN";
83  break;
84  case FUNCTION_5:
85  mode_s = "FUNCTION_5";
86  break;
87  case FUNCTION_6:
88  mode_s = "FUNCTION_6";
89  break;
90  case ANALOG:
91  mode_s = "ANALOG";
92  break;
93 #endif
94 #ifdef ARDUINO_ARCH_ESP8266
95  case FUNCTION_0:
96  mode_s = "FUNCTION_0";
97  break;
98  case WAKEUP_PULLUP:
99  mode_s = "WAKEUP_PULLUP";
100  break;
101  case WAKEUP_PULLDOWN:
102  mode_s = "WAKEUP_PULLDOWN";
103  break;
104  case INPUT_PULLDOWN_16:
105  mode_s = "INPUT_PULLDOWN_16";
106  break;
107 #endif
108 
109  default:
110  mode_s = "UNKNOWN";
111  break;
112  }
113 
114  return mode_s;
115 }
116 
117 unsigned char GPIOPin::get_pin() const { return this->pin_; }
118 unsigned char GPIOPin::get_mode() const { return this->mode_; }
119 
120 bool GPIOPin::is_inverted() const { return this->inverted_; }
121 void GPIOPin::setup() { this->pin_mode(this->mode_); }
122 bool ICACHE_RAM_ATTR HOT GPIOPin::digital_read() {
123  return bool((*this->gpio_read_) & this->gpio_mask_) != this->inverted_;
124 }
125 bool ICACHE_RAM_ATTR HOT ISRInternalGPIOPin::digital_read() {
126  return bool((*this->gpio_read_) & this->gpio_mask_) != this->inverted_;
127 }
128 void ICACHE_RAM_ATTR HOT GPIOPin::digital_write(bool value) {
129 #ifdef ARDUINO_ARCH_ESP8266
130  if (this->pin_ != 16) {
131  if (value != this->inverted_) {
132  GPOS = this->gpio_mask_;
133  } else {
134  GPOC = this->gpio_mask_;
135  }
136  } else {
137  if (value != this->inverted_) {
138  GP16O |= 1;
139  } else {
140  GP16O &= ~1;
141  }
142  }
143 #endif
144 #ifdef ARDUINO_ARCH_ESP32
145  if (value != this->inverted_) {
146  (*this->gpio_set_) = this->gpio_mask_;
147  } else {
148  (*this->gpio_clear_) = this->gpio_mask_;
149  }
150 #endif
151 }
152 void ICACHE_RAM_ATTR HOT ISRInternalGPIOPin::digital_write(bool value) {
153 #ifdef ARDUINO_ARCH_ESP8266
154  if (this->pin_ != 16) {
155  if (value != this->inverted_) {
156  GPOS = this->gpio_mask_;
157  } else {
158  GPOC = this->gpio_mask_;
159  }
160  } else {
161  if (value != this->inverted_) {
162  GP16O |= 1;
163  } else {
164  GP16O &= ~1;
165  }
166  }
167 #endif
168 #ifdef ARDUINO_ARCH_ESP32
169  if (value != this->inverted_) {
170  (*this->gpio_set_) = this->gpio_mask_;
171  } else {
172  (*this->gpio_clear_) = this->gpio_mask_;
173  }
174 #endif
175 }
177 #ifdef ARDUINO_ARCH_ESP32
178  volatile uint32_t *gpio_clear, volatile uint32_t *gpio_set,
179 #endif
180  volatile uint32_t *gpio_read, uint32_t gpio_mask, bool inverted)
181  : pin_(pin),
182  inverted_(inverted),
183  gpio_read_(gpio_read),
184  gpio_mask_(gpio_mask)
185 #ifdef ARDUINO_ARCH_ESP32
186  ,
187  gpio_clear_(gpio_clear),
188  gpio_set_(gpio_set)
189 #endif
190 {
191 }
192 void ICACHE_RAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
193 #ifdef ARDUINO_ARCH_ESP8266
194  GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, this->gpio_mask_);
195 #endif
196 #ifdef ARDUINO_ARCH_ESP32
197  if (this->pin_ < 32) {
198  GPIO.status_w1tc = this->gpio_mask_;
199  } else {
200  GPIO.status1_w1tc.intr_st = this->gpio_mask_;
201  }
202 #endif
203 }
204 
205 void ICACHE_RAM_ATTR HOT GPIOPin::pin_mode(uint8_t mode) {
206 #ifdef ARDUINO_ARCH_ESP8266
207  if (this->pin_ == 16 && mode == INPUT_PULLUP) {
208  // pullups are not available on GPIO16, manually override with
209  // input mode.
210  pinMode(16, INPUT);
211  return;
212  }
213 #endif
214  pinMode(this->pin_, mode);
215 }
216 
217 #ifdef ARDUINO_ARCH_ESP8266
218 struct ESPHomeInterruptFuncInfo {
219  void (*func)(void *);
220  void *arg;
221 };
222 
223 void ICACHE_RAM_ATTR interrupt_handler(void *arg) {
224  ArgStructure *as = static_cast<ArgStructure *>(arg);
225  auto *info = static_cast<ESPHomeInterruptFuncInfo *>(as->functionInfo);
226  info->func(info->arg);
227 }
228 #endif
229 
230 void GPIOPin::detach_interrupt() const { this->detach_interrupt_(); }
232 #ifdef ARDUINO_ARCH_ESP8266
233  __detachInterrupt(get_pin());
234 #endif
235 #ifdef ARDUINO_ARCH_ESP32
236  detachInterrupt(get_pin());
237 #endif
238 }
239 void GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, int mode) const {
240  if (this->inverted_) {
241  if (mode == RISING) {
242  mode = FALLING;
243  } else if (mode == FALLING) {
244  mode = RISING;
245  }
246  }
247 #ifdef ARDUINO_ARCH_ESP8266
248  ArgStructure *as = new ArgStructure;
249  as->interruptInfo = nullptr;
250 
251  as->functionInfo = new ESPHomeInterruptFuncInfo{
252  .func = func,
253  .arg = arg,
254  };
255 
256  __attachInterruptArg(this->pin_, interrupt_handler, as, mode);
257 #endif
258 #ifdef ARDUINO_ARCH_ESP32
259  // work around issue https://github.com/espressif/arduino-esp32/pull/1776 in arduino core
260  // yet again proves how horrible code is there :( - how could that have been accepted...
261  auto *attach = reinterpret_cast<void (*)(uint8_t, void (*)(void *), void *, int)>(attachInterruptArg);
262  attach(this->pin_, func, arg, mode);
263 #endif
264 }
265 
267  return new ISRInternalGPIOPin(this->pin_,
268 #ifdef ARDUINO_ARCH_ESP32
269  this->gpio_clear_, this->gpio_set_,
270 #endif
271  this->gpio_read_, this->gpio_mask_, this->inverted_);
272 }
273 
275 #ifdef ARDUINO_ARCH_ESP8266
276  // Tasmota uses magic bytes in the binary to check if an OTA firmware is compatible
277  // with their settings - ESPHome uses a different settings system (that can also survive
278  // erases). So set magic bytes indicating all tasmota versions are supported.
279  // This only adds 12 bytes of binary size, which is an acceptable price to pay for easier support
280  // for Tasmota.
281  // https://github.com/arendst/Tasmota/blob/b05301b1497942167a015a6113b7f424e42942cd/tasmota/settings.ino#L346-L380
282  // https://github.com/arendst/Tasmota/blob/b05301b1497942167a015a6113b7f424e42942cd/tasmota/i18n.h#L652-L654
283  const static uint32_t TASMOTA_MAGIC_BYTES[] PROGMEM = {0x5AA55AA5, 0xFFFFFFFF, 0xA55AA55A};
284  // Force link symbol by using a volatile integer (GCC attribute used does not work because of LTO)
285  volatile int x = 0;
286  x = TASMOTA_MAGIC_BYTES[x];
287 #endif
288 }
289 
290 } // namespace esphome
291 
292 #ifdef ARDUINO_ESP8266_RELEASE_2_3_0
293 // Fix 2.3.0 std missing memchr
294 extern "C" {
295 void *memchr(const void *s, int c, size_t n) {
296  if (n == 0)
297  return nullptr;
298  const uint8_t *p = reinterpret_cast<const uint8_t *>(s);
299  do {
300  if (*p++ == c)
301  return const_cast<void *>(reinterpret_cast<const void *>(p - 1));
302  } while (--n != 0);
303  return nullptr;
304 }
305 };
306 #endif
307 
308 #ifdef ARDUINO_ARCH_ESP8266
309 extern "C" {
310 extern void resetPins() { // NOLINT
311  // Added in framework 2.7.0
312  // usually this sets up all pins to be in INPUT mode
313  // however, not strictly needed as we set up the pins properly
314  // ourselves and this causes pins to toggle during reboot.
315 }
316 }
317 #endif
GPIOPin(uint8_t pin, uint8_t mode, bool inverted=false)
Construct the GPIOPin instance.
Definition: esphal.cpp:23
void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, void(*)(void *), void *fp, int mode)
Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
Definition: esphal.h:40
void digital_write(bool value)
Definition: esphal.cpp:152
const char * TAG
Definition: tm1637.cpp:8
uint8_t get_mode() const
Get the pinMode of this pin.
const uint32_t gpio_mask_
Definition: esphal.h:113
volatile uint32_t *const gpio_clear_
Definition: esphal.h:110
volatile uint32_t *const gpio_read_
Definition: esphal.h:112
volatile uint32_t *const gpio_clear_
Definition: esphal.h:57
void attach_interrupt_(void(*func)(void *), void *arg, int mode) const
Definition: esphal.cpp:239
volatile uint32_t *const gpio_set_
Definition: esphal.h:109
const uint32_t gpio_mask_
Definition: esphal.h:55
bool is_inverted() const
Return whether this pin shall be treated as inverted. (for example active-low)
const uint8_t mode_
Definition: esphal.h:106
const uint8_t pin_
Definition: esphal.h:105
const bool inverted_
Definition: esphal.h:107
virtual bool digital_read()
Read the binary value from this pin using digitalRead (and inverts automatically).
Definition: esphal.cpp:122
void detach_interrupt_() const
Definition: esphal.cpp:231
void ICACHE_RAM_ATTR interrupt_handler(void *arg)
Definition: esphal.cpp:223
const float BME680_GAS_LOOKUP_TABLE_1 [16] PROGMEM
Definition: bme680.cpp:24
volatile uint32_t *const gpio_set_
Definition: esphal.h:58
virtual void digital_write(bool value)
Write the binary value to this pin using digitalWrite (and inverts automatically).
Definition: esphal.cpp:128
ISRInternalGPIOPin(uint8_t pin, #ifdef ARDUINO_ARCH_ESP32 volatile uint32_t *gpio_clear, volatile uint32_t *gpio_set, #endif volatile uint32_t *gpio_read, uint32_t gpio_mask, bool inverted)
Definition: esphal.cpp:176
virtual void setup()
Setup the pin mode.
virtual void pin_mode(uint8_t mode)
Set the pin mode.
Definition: esphal.cpp:205
void force_link_symbols()
This function can be used by the HAL to force-link specific symbols into the generated binary without...
Definition: esphal.cpp:274
uint8_t get_pin() const
Get the GPIO pin number.
void detach_interrupt() const
Definition: esphal.cpp:230
void resetPins()
Definition: esphal.cpp:310
ClimateMode mode
Definition: climate.h:344
Definition: a4988.cpp:4
const uint8_t pin_
Definition: esphal.h:52
ISRInternalGPIOPin * to_isr() const
Definition: esphal.cpp:266
void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin)
void * memchr(const void *s, int c, size_t n)
Definition: esphal.cpp:295
volatile uint32_t *const gpio_read_
Definition: esphal.h:54
const char * get_pin_mode_name() const