ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
gpio.cpp
Go to the documentation of this file.
1#ifdef USE_ZEPHYR
2#include "gpio.h"
3#include <zephyr/drivers/gpio.h>
4#include <zephyr/sys/util.h>
5#include "esphome/core/log.h"
6
7namespace esphome {
8namespace zephyr {
9
10static const char *const TAG = "zephyr";
11
12static gpio_flags_t flags_to_mode(gpio::Flags flags, bool inverted, bool value) {
13 gpio_flags_t ret = 0;
14 if (flags & gpio::FLAG_INPUT) {
15 ret |= GPIO_INPUT;
16 }
18 ret |= GPIO_OUTPUT;
19 if (value != inverted) {
20 ret |= GPIO_OUTPUT_INIT_HIGH;
21 } else {
22 ret |= GPIO_OUTPUT_INIT_LOW;
23 }
24 }
26 ret |= GPIO_PULL_UP;
27 }
29 ret |= GPIO_PULL_DOWN;
30 }
32 ret |= GPIO_OPEN_DRAIN;
33 }
34 return ret;
35}
36
37// ESPHome's InterruptType is expressed in logical levels, but the pin is configured active-high in Zephyr (inversion is
38// applied in software by digital_read()/digital_write(), see the `!= inverted_` convention below). So when the pin is
39// inverted we must swap the physical edge/level the interrupt arms on: a logical rising edge is a physical falling
40// edge, etc. GPIO_INT_EDGE_BOTH is symmetric and needs no swap.
41static gpio_flags_t interrupt_type_to_flags(gpio::InterruptType type, bool inverted) {
42 switch (type) {
44 return inverted ? GPIO_INT_EDGE_FALLING : GPIO_INT_EDGE_RISING;
46 return inverted ? GPIO_INT_EDGE_RISING : GPIO_INT_EDGE_FALLING;
48 return GPIO_INT_EDGE_BOTH;
50 return inverted ? GPIO_INT_LEVEL_HIGH : GPIO_INT_LEVEL_LOW;
52 return inverted ? GPIO_INT_LEVEL_LOW : GPIO_INT_LEVEL_HIGH;
53 }
54 return inverted ? GPIO_INT_EDGE_FALLING : GPIO_INT_EDGE_RISING;
55}
56
57// Zephyr calls this with a pointer to the gpio_callback the interrupt fired on.
58// Recover the owning ZephyrGPIOInterrupt and dispatch to the ESPHome ISR.
59static void gpio_interrupt_handler(const device * /*dev*/, gpio_callback *cb, uint32_t /*pins*/) {
60 auto *interrupt = CONTAINER_OF(cb, ZephyrGPIOInterrupt, callback);
61 if (interrupt->func != nullptr) {
62 interrupt->func(interrupt->arg);
63 }
64}
65
66struct ISRPinArg {
67 const device *gpio;
68 uint8_t pin;
69 uint8_t gpio_size;
70 bool inverted;
71};
72
74 auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
75 arg->gpio = this->gpio_;
76 arg->pin = this->pin_;
77 arg->gpio_size = this->gpio_size_;
78 arg->inverted = this->inverted_;
79 return ISRInternalGPIOPin((void *) arg);
80}
81
82void ZephyrGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
83 if (!device_is_ready(this->gpio_)) {
84 ESP_LOGE(TAG, "Cannot attach interrupt: GPIO device not ready");
85 return;
86 }
87
88 // Drop any interrupt previously attached to this pin before re-registering.
89 this->detach_interrupt();
90
91 this->interrupt_.func = func;
92 this->interrupt_.arg = arg;
93
94 uint8_t port_pin = this->pin_ % this->gpio_size_;
95 gpio_init_callback(&this->interrupt_.callback, gpio_interrupt_handler, BIT(port_pin));
96
97 int ret = gpio_add_callback(this->gpio_, &this->interrupt_.callback);
98 if (ret != 0) {
99 ESP_LOGE(TAG, "gpio_add_callback failed for pin %u: %d", this->pin_, ret);
100 return;
101 }
102
103 ret = gpio_pin_interrupt_configure(this->gpio_, port_pin, interrupt_type_to_flags(type, this->inverted_));
104 if (ret != 0) {
105 ESP_LOGE(TAG, "gpio_pin_interrupt_configure failed for pin %u: %d", this->pin_, ret);
106 gpio_remove_callback(this->gpio_, &this->interrupt_.callback);
107 return;
108 }
109
110 ESP_LOGD(TAG, "Interrupt attached to pin %u (type=%d)", this->pin_, (int) type);
111}
112
114 if (!device_is_ready(this->gpio_)) {
115 ESP_LOGE(TAG, "gpio %u is not ready.", this->pin_);
116 return;
117 }
118 this->pin_mode(this->flags_);
119}
120
122 if (nullptr == this->gpio_) {
123 return;
124 }
125 auto ret = gpio_pin_configure(this->gpio_, this->pin_ % this->gpio_size_,
126 flags_to_mode(flags, this->inverted_, this->value_));
127 if (ret != 0) {
128 ESP_LOGE(TAG, "gpio %u cannot be configured %d.", this->pin_, ret);
129 }
130}
131
132size_t ZephyrGPIOPin::dump_summary(char *buffer, size_t len) const {
133 return snprintf(buffer, len, "GPIO%u, %s%u", this->pin_, this->pin_name_prefix_, this->pin_ % this->gpio_size_);
134}
135
137 if (nullptr == this->gpio_) {
138 return false;
139 }
140 return bool(gpio_pin_get(this->gpio_, this->pin_ % this->gpio_size_) != this->inverted_);
141}
142
144 // make sure that value is not ignored since it can be inverted e.g. on switch side
145 // that way init state should be correct
146 this->value_ = value;
147 if (nullptr == this->gpio_) {
148 return;
149 }
150 gpio_pin_set(this->gpio_, this->pin_ % this->gpio_size_, value != this->inverted_ ? 1 : 0);
151}
152
154 if (this->gpio_ == nullptr) {
155 return;
156 }
157
158 uint8_t port_pin = this->pin_ % this->gpio_size_;
159 gpio_pin_interrupt_configure(this->gpio_, port_pin, GPIO_INT_DISABLE);
160 gpio_remove_callback(this->gpio_, &this->interrupt_.callback);
161
162 this->interrupt_.func = nullptr;
163 this->interrupt_.arg = nullptr;
164}
165
166} // namespace zephyr
167
168bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
169 auto *arg = (zephyr::ISRPinArg *) this->arg_;
170 if (arg == nullptr || arg->gpio == nullptr) {
171 return false;
172 }
173 return bool(gpio_pin_get(arg->gpio, arg->pin % arg->gpio_size) != arg->inverted);
174}
175
176void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
177 auto *arg = (zephyr::ISRPinArg *) this->arg_;
178 if (arg == nullptr || arg->gpio == nullptr) {
179 return;
180 }
181 gpio_pin_set(arg->gpio, arg->pin % arg->gpio_size, value != arg->inverted ? 1 : 0);
182}
183
184} // namespace esphome
185
186#endif
Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
Definition gpio.h:85
void digital_write(bool value)
Definition gpio.cpp:148
const device * gpio_
Definition gpio.h:43
void digital_write(bool value) override
Definition gpio.cpp:143
void attach_interrupt(void(*func)(void *), void *arg, gpio::InterruptType type) const override
Definition gpio.cpp:82
const char * pin_name_prefix_
Definition gpio.h:44
bool digital_read() override
Definition gpio.cpp:136
size_t dump_summary(char *buffer, size_t len) const override
Definition gpio.cpp:132
ISRInternalGPIOPin to_isr() const override
Definition gpio.cpp:73
ZephyrGPIOInterrupt interrupt_
Definition gpio.h:53
void detach_interrupt() const override
Definition gpio.cpp:153
void pin_mode(gpio::Flags flags) override
Definition gpio.cpp:121
uint16_t type
uint16_t flags
@ INTERRUPT_FALLING_EDGE
Definition gpio.h:49
@ INTERRUPT_RISING_EDGE
Definition gpio.h:48
@ INTERRUPT_HIGH_LEVEL
Definition gpio.h:52
@ INTERRUPT_LOW_LEVEL
Definition gpio.h:51
@ INTERRUPT_ANY_EDGE
Definition gpio.h:50
@ FLAG_OUTPUT
Definition gpio.h:26
@ FLAG_OPEN_DRAIN
Definition gpio.h:27
@ FLAG_PULLUP
Definition gpio.h:28
@ FLAG_INPUT
Definition gpio.h:25
@ FLAG_PULLDOWN
Definition gpio.h:29
const char *const TAG
Definition spi.cpp:7
const void size_t len
Definition hal.h:64
static void uint32_t
struct gpio_callback callback
Definition gpio.h:14