Skip to content

Commit ce6378f

Browse files
committed
feat(interrupt): Improve Interrupt implementation and API
* Add `enable` / `disable` APIs for specific interrutps * Add `remove` APIs for specific interrupts * Add `remove_all` * Add `disable_all` * Refactor how interrupts are configured somewhat to ensure they are not enabled until after the handler has been registered * Update interrupt PinConfig to have `auto_reenable` flag (default=true) to mirror previous behavior, but to allow the interrupt to not re-trigger until explicitly re-enabled by the caller. * Add more info about interrupt configuration to class doc
1 parent babba1e commit ce6378f

File tree

1 file changed

+164
-23
lines changed

1 file changed

+164
-23
lines changed

components/interrupt/include/interrupt.hpp

Lines changed: 164 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,22 @@ namespace espp {
4747
/// the each other, while ensuring that the interupts are still
4848
/// processed in an orderly fashion.
4949
///
50+
/// If CONFIG_GPIO_CTRL_FUNC_IN_IRAM is enabled, then the ISR handler
51+
/// will be placed in IRAM. In this condition, the interrupt class' ISR
52+
/// handler will automatically disable the interrupt associated with
53+
/// that GPIO within the ISR handler.
54+
///
55+
/// If CONFIG_GPIO_CTRL_FUNC_IN_IRAM is enabled and the PinConfig has
56+
/// auto_reenable set to false, then the interrupt will not be
57+
/// reenabled automatically. This is because the ISR handler will
58+
/// disable the interrupt, and it will not be reenabled until the user
59+
/// reenables it. This use-case is recommended for ACTIVE_LOW or
60+
/// ACTIVE_HIGH interrupts which may need some other action to clear
61+
/// the interrupt condition and prevent the ISR from being triggered
62+
/// continuously. In this case, simply re-enable the interrupt when you
63+
/// have cleared the interrupt condition if you want to be able to
64+
/// respond to it again.
65+
///
5066
/// \section interrupt_ex0 Interrupt Example
5167
/// \snippet interrupt_example.cpp interrupt example
5268
class Interrupt : public BaseComponent {
@@ -104,6 +120,13 @@ class Interrupt : public BaseComponent {
104120
struct PinConfig {
105121
int gpio_num; ///< GPIO number to for this interrupt
106122
event_callback_fn callback; ///< Callback for the interrupt event
123+
bool auto_reenable = true; ///< Whether to auto reenable the
124+
/// interrupt after it is triggered.
125+
/// If false, the interrupt will
126+
/// need to be < reenabled in the
127+
/// callback or some other codepath.
128+
/// If true, the interrupt will < be
129+
/// reenabled automatically.
107130
ActiveLevel active_level; ///< Active level of the GPIO
108131
Type interrupt_type = Type::ANY_EDGE; ///< Interrupt type to use for the GPIO
109132
bool pullup_enabled = false; ///< Whether to enable the pullup resistor
@@ -160,12 +183,7 @@ class Interrupt : public BaseComponent {
160183
/// \brief Destructor
161184
~Interrupt() {
162185
// remove the isr handlers
163-
{
164-
std::lock_guard<std::recursive_mutex> lock(interrupt_mutex_);
165-
for (const auto &args : handler_args_) {
166-
gpio_isr_handler_remove(static_cast<gpio_num_t>(args->gpio_num));
167-
}
168-
}
186+
remove_all();
169187
if (queue_) {
170188
// send to the event queue to wake up the task
171189
EventData event_data = {-1};
@@ -175,18 +193,6 @@ class Interrupt : public BaseComponent {
175193
// delete the queue
176194
vQueueDelete(queue_);
177195
}
178-
#if CONFIG_SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER || CONFIG_SOC_GPIO_FLEX_GLITCH_FILTER_NUM > 0
179-
for (const auto &handle : glitch_filter_handles_) {
180-
// disable the glitch filters
181-
gpio_glitch_filter_disable(handle);
182-
// and delete the handle
183-
gpio_del_glitch_filter(handle);
184-
}
185-
#endif
186-
// now delete the handler args
187-
for (const auto &args : handler_args_) {
188-
delete args;
189-
}
190196
}
191197

192198
/// \brief Get the minimum number of free spaces in the queue
@@ -210,6 +216,126 @@ class Interrupt : public BaseComponent {
210216
configure_interrupt(interrupt);
211217
}
212218

219+
/// \brief Remove all the interrupts from the interrupt handler
220+
/// \details This will remove all the interrupts that are currently registered
221+
/// with the interrupt handler. This will also disable all the
222+
/// interrupts that are currently enabled.
223+
void remove_all() {
224+
std::lock_guard<std::recursive_mutex> lock(interrupt_mutex_);
225+
for (const auto &interrupt : interrupts_) {
226+
disable_interrupt(interrupt);
227+
}
228+
// clear the interrupts vector
229+
interrupts_.clear();
230+
// delete the handler args objects
231+
for (const auto &args : handler_args_) {
232+
delete args;
233+
}
234+
// clear the handler args vector
235+
handler_args_.clear();
236+
// disable anly glitch filters if they are enabled
237+
#if CONFIG_SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER || CONFIG_SOC_GPIO_FLEX_GLITCH_FILTER_NUM > 0
238+
for (const auto &handle : glitch_filter_handles_) {
239+
// disable the glitch filters
240+
gpio_glitch_filter_disable(handle);
241+
// and delete the handle
242+
gpio_del_glitch_filter(handle);
243+
}
244+
#endif
245+
// clear the glitch filter handles vector
246+
glitch_filter_handles_.clear();
247+
}
248+
249+
/// \brief Remove an interrupt from the interrupt handler
250+
/// \param interrupt The interrupt to remove
251+
/// \details This will find a registered interrupt with the given GPIO number
252+
/// and remove it from the list of interrupts. It will also disable
253+
/// the interrupt for that GPIO number. If no interrupt is found with
254+
/// the given GPIO number, then nothing is done.
255+
void remove_interrupt(const PinConfig &interrupt) { remove_interrupt(interrupt.gpio_num); }
256+
257+
/// \brief Remove an interrupt from the interrupt handler
258+
/// \param gpio_num The GPIO number of the interrupt to remove
259+
/// \details This will find a registered interrupt with the given GPIO number
260+
/// and remove it from the list of interrupts. It will also disable
261+
/// the interrupt for that GPIO number. If no interrupt is found with
262+
/// the given GPIO number, then nothing is done.
263+
void remove_interrupt(int gpio_num) {
264+
logger_.info("Removing interrupt for GPIO {}", gpio_num);
265+
// disable the interrupt
266+
gpio_intr_disable(static_cast<gpio_num_t>(gpio_num));
267+
// remove the interrupt from the list
268+
std::lock_guard<std::recursive_mutex> lock(interrupt_mutex_);
269+
auto predicate = [gpio_num](const PinConfig &interrupt) {
270+
return interrupt.gpio_num == gpio_num;
271+
};
272+
auto interrupt = std::find_if(interrupts_.begin(), interrupts_.end(), predicate);
273+
if (interrupt == interrupts_.end()) {
274+
logger_.error("No interrupt found for GPIO {}", gpio_num);
275+
return;
276+
}
277+
// erase the interrupt
278+
interrupts_.erase(interrupt);
279+
// remove the isr handler
280+
gpio_isr_handler_remove(static_cast<gpio_num_t>(gpio_num));
281+
// erase the handler args object that corresponds to the gpio_num
282+
auto handler_arg =
283+
std::find_if(handler_args_.begin(), handler_args_.end(),
284+
[gpio_num](const HandlerArgs *args) { return args->gpio_num == gpio_num; });
285+
if (handler_arg != handler_args_.end()) {
286+
delete *handler_arg;
287+
handler_args_.erase(handler_arg);
288+
} else {
289+
logger_.error("No handler args found for GPIO {}", gpio_num);
290+
}
291+
}
292+
293+
/// \brief Disable all the interrupts
294+
/// \details This will disable all the interrupts that are currently
295+
/// registered with the interrupt handler. This will not remove the
296+
/// interrupts from the list of interrupts, so they can be reenabled
297+
/// later.
298+
void disable_all() {
299+
std::lock_guard<std::recursive_mutex> lock(interrupt_mutex_);
300+
for (const auto &interrupt : interrupts_) {
301+
disable_interrupt(interrupt);
302+
}
303+
}
304+
305+
/// \brief Disable the interrupt for the interrupt PinConfig
306+
/// \param interrupt The interrupt to disable
307+
/// \details This will disable the interrupt for the GPIO that is specified,
308+
/// regardless of whether the interrupt is in the list of interrupts
309+
/// or not.
310+
void disable_interrupt(const PinConfig &interrupt) { disable_interrupt(interrupt.gpio_num); }
311+
312+
/// \brief Disable the interrupt for the GPIO
313+
/// \param gpio_num The GPIO number of the interrupt to disable
314+
/// \details This will disable the interrupt for the GPIO that is specified,
315+
/// regardless of whether the interrupt is in the list of interrupts
316+
/// or not.
317+
void disable_interrupt(int gpio_num) {
318+
logger_.debug("Disabling interrupt for GPIO {}", gpio_num);
319+
gpio_intr_disable(static_cast<gpio_num_t>(gpio_num));
320+
}
321+
322+
/// \brief Enable the interrupt for the GPIO
323+
/// \param interrupt The interrupt to enable
324+
/// \details This will enable the interrupt for the GPIO that is specified,
325+
/// regardless of whether the interrupt is in the list of interrupts
326+
/// or not.
327+
void enable_interrupt(const PinConfig &interrupt) { enable_interrupt(interrupt.gpio_num); }
328+
329+
/// \brief Enable the interrupt for the GPIO
330+
/// \param gpio_num The GPIO number of the interrupt to enable
331+
/// \details This will enable the interrupt for the GPIO that is specified,
332+
/// regardless of whether the interrupt is in the list of interrupts
333+
/// or not.
334+
void enable_interrupt(int gpio_num) {
335+
logger_.debug("Enabling interrupt for GPIO {}", gpio_num);
336+
gpio_intr_enable(static_cast<gpio_num_t>(gpio_num));
337+
}
338+
213339
/// \brief Get the state of the interrupt
214340
/// \param interrupt The interrupt to check
215341
/// \return Whether the interrupt is active
@@ -263,9 +389,17 @@ class Interrupt : public BaseComponent {
263389
};
264390

265391
static void isr_handler(void *arg) {
392+
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
266393
auto *args = static_cast<HandlerArgs *>(arg);
394+
#if CONFIG_GPIO_CTRL_FUNC_IN_IRAM
395+
// disable the interrupt for the pin
396+
gpio_intr_disable(static_cast<gpio_num_t>(args->gpio_num));
397+
#endif
267398
EventData event_data = {args->gpio_num};
268-
xQueueSendFromISR(args->event_queue, &event_data, nullptr);
399+
xQueueSendFromISR(args->event_queue, &event_data, &xHigherPriorityTaskWoken);
400+
if (xHigherPriorityTaskWoken) {
401+
portYIELD_FROM_ISR();
402+
}
269403
}
270404

271405
bool is_active_level(int gpio_num, ActiveLevel active_level) const {
@@ -296,6 +430,12 @@ class Interrupt : public BaseComponent {
296430
logger_.error("No interrupt found for GPIO {}", event_data.gpio_num);
297431
return false;
298432
}
433+
#if CONFIG_GPIO_CTRL_FUNC_IN_IRAM
434+
if (interrupt->auto_reenable) {
435+
logger_.debug("Auto-reenabling interrupt for GPIO {}", event_data.gpio_num);
436+
gpio_intr_enable(static_cast<gpio_num_t>(event_data.gpio_num));
437+
}
438+
#endif
299439
if (!interrupt->callback) {
300440
logger_.error("No callback registered for GPIO {}", event_data.gpio_num);
301441
return false;
@@ -372,17 +512,17 @@ class Interrupt : public BaseComponent {
372512
io_conf.pull_up_en = interrupt.pullup_enabled ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
373513
io_conf.pull_down_en =
374514
interrupt.pulldown_enabled ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE;
375-
gpio_config(&io_conf);
376515
{
377516
std::lock_guard<std::recursive_mutex> lock(interrupt_mutex_);
378517
// add the isr handler
379518
HandlerArgs *handler_arg = new HandlerArgs{interrupt.gpio_num, queue_};
380519
handler_args_.push_back(handler_arg);
381-
gpio_isr_handler_add(static_cast<gpio_num_t>(interrupt.gpio_num), isr_handler,
382-
static_cast<void *>(handler_arg));
520+
gpio_num_t gpio_num = static_cast<gpio_num_t>(interrupt.gpio_num);
521+
gpio_isr_handler_add(gpio_num, isr_handler, static_cast<void *>(handler_arg));
383522
// configure the filter if needed
384523
configure_filter(interrupt);
385524
}
525+
gpio_config(&io_conf);
386526
}
387527

388528
static bool ISR_SERVICE_INSTALLED;
@@ -392,7 +532,8 @@ class Interrupt : public BaseComponent {
392532
std::recursive_mutex interrupt_mutex_;
393533
std::vector<PinConfig> interrupts_;
394534
std::vector<HandlerArgs *> handler_args_;
395-
std::vector<gpio_glitch_filter_handle_t> glitch_filter_handles_;
535+
std::vector<gpio_glitch_filter_handle_t> glitch_filter_handles_; // TODO: remove these when the
536+
// interrupts are removed
396537
std::unique_ptr<Task> task_;
397538
};
398539
} // namespace espp

0 commit comments

Comments
 (0)