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