From ef47dfeeaf8c73e976bd22846c6a3b8702bc031a Mon Sep 17 00:00:00 2001 From: Graham Sanderson Date: Wed, 8 Jun 2022 13:03:11 -0500 Subject: [PATCH] Add new GPIO APIs for adding shared GPIO handlers, and improve docs (#850) Co-authored-by: Adam Nielsen --- src/rp2_common/hardware_gpio/CMakeLists.txt | 3 +- src/rp2_common/hardware_gpio/gpio.c | 100 ++++-- .../hardware_gpio/include/hardware/gpio.h | 293 ++++++++++++++++-- 3 files changed, 330 insertions(+), 66 deletions(-) diff --git a/src/rp2_common/hardware_gpio/CMakeLists.txt b/src/rp2_common/hardware_gpio/CMakeLists.txt index 1bfb078..97a9355 100644 --- a/src/rp2_common/hardware_gpio/CMakeLists.txt +++ b/src/rp2_common/hardware_gpio/CMakeLists.txt @@ -1 +1,2 @@ -pico_simple_hardware_target(gpio) \ No newline at end of file +pico_simple_hardware_target(gpio) +target_link_libraries(hardware_gpio INTERFACE hardware_irq) \ No newline at end of file diff --git a/src/rp2_common/hardware_gpio/gpio.c b/src/rp2_common/hardware_gpio/gpio.c index 4197a4b..aa6e98c 100644 --- a/src/rp2_common/hardware_gpio/gpio.c +++ b/src/rp2_common/hardware_gpio/gpio.c @@ -14,11 +14,13 @@ #include "pico/binary_info.h" #endif -static gpio_irq_callback_t _callbacks[NUM_CORES]; +static gpio_irq_callback_t callbacks[NUM_CORES]; +// a 1 bit means the IRQ is handled by a raw IRQ handler +static uint32_t raw_irq_mask[NUM_CORES]; // Get the raw value from the pin, bypassing any muxing or overrides. int gpio_get_pad(uint gpio) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); hw_set_bits(&padsbank0_hw->io[gpio], PADS_BANK0_GPIO0_IE_BITS); return (iobank0_hw->io[gpio].status & IO_BANK0_GPIO0_STATUS_INFROMPAD_BITS) >> IO_BANK0_GPIO0_STATUS_INFROMPAD_LSB; @@ -28,7 +30,7 @@ int gpio_get_pad(uint gpio) { // Select function for this GPIO, and ensure input/output are enabled at the pad. // This also clears the input/output/irq override bits. void gpio_set_function(uint gpio, enum gpio_function fn) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); invalid_params_if(GPIO, ((uint32_t)fn << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) & ~IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS); // Set input enable on, output disable off hw_write_masked(&padsbank0_hw->io[gpio], @@ -42,14 +44,14 @@ void gpio_set_function(uint gpio, enum gpio_function fn) { /// \end::gpio_set_function[] enum gpio_function gpio_get_function(uint gpio) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); return (enum gpio_function) ((iobank0_hw->io[gpio].ctrl & IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) >> IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB); } // Note that, on RP2040, setting both pulls enables a "bus keep" function, // i.e. weak pull to whatever is current high/low state of GPIO. void gpio_set_pulls(uint gpio, bool up, bool down) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); hw_write_masked( &padsbank0_hw->io[gpio], (bool_to_bit(up) << PADS_BANK0_GPIO0_PUE_LSB) | (bool_to_bit(down) << PADS_BANK0_GPIO0_PDE_LSB), @@ -59,7 +61,7 @@ void gpio_set_pulls(uint gpio, bool up, bool down) { // Direct override for per-GPIO IRQ signal void gpio_set_irqover(uint gpio, uint value) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); hw_write_masked(&iobank0_hw->io[gpio].ctrl, value << IO_BANK0_GPIO0_CTRL_IRQOVER_LSB, IO_BANK0_GPIO0_CTRL_IRQOVER_BITS @@ -68,7 +70,7 @@ void gpio_set_irqover(uint gpio, uint value) { // Direct overrides for pad controls void gpio_set_inover(uint gpio, uint value) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); hw_write_masked(&iobank0_hw->io[gpio].ctrl, value << IO_BANK0_GPIO0_CTRL_INOVER_LSB, IO_BANK0_GPIO0_CTRL_INOVER_BITS @@ -76,7 +78,7 @@ void gpio_set_inover(uint gpio, uint value) { } void gpio_set_outover(uint gpio, uint value) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); hw_write_masked(&iobank0_hw->io[gpio].ctrl, value << IO_BANK0_GPIO0_CTRL_OUTOVER_LSB, IO_BANK0_GPIO0_CTRL_OUTOVER_BITS @@ -84,7 +86,7 @@ void gpio_set_outover(uint gpio, uint value) { } void gpio_set_oeover(uint gpio, uint value) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); hw_write_masked(&iobank0_hw->io[gpio].ctrl, value << IO_BANK0_GPIO0_CTRL_OEOVER_LSB, IO_BANK0_GPIO0_CTRL_OEOVER_BITS @@ -92,7 +94,7 @@ void gpio_set_oeover(uint gpio, uint value) { } void gpio_set_input_hysteresis_enabled(uint gpio, bool enabled) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); if (enabled) hw_set_bits(&padsbank0_hw->io[gpio], PADS_BANK0_GPIO0_SCHMITT_BITS); else @@ -101,12 +103,12 @@ void gpio_set_input_hysteresis_enabled(uint gpio, bool enabled) { bool gpio_is_input_hysteresis_enabled(uint gpio) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); return (padsbank0_hw->io[gpio] & PADS_BANK0_GPIO0_SCHMITT_BITS) != 0; } void gpio_set_slew_rate(uint gpio, enum gpio_slew_rate slew) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); hw_write_masked(&padsbank0_hw->io[gpio], (uint)slew << PADS_BANK0_GPIO0_SLEWFAST_LSB, PADS_BANK0_GPIO0_SLEWFAST_BITS @@ -114,7 +116,7 @@ void gpio_set_slew_rate(uint gpio, enum gpio_slew_rate slew) { } enum gpio_slew_rate gpio_get_slew_rate(uint gpio) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); return (enum gpio_slew_rate)((padsbank0_hw->io[gpio] & PADS_BANK0_GPIO0_SLEWFAST_BITS) >> PADS_BANK0_GPIO0_SLEWFAST_LSB); @@ -124,7 +126,7 @@ enum gpio_slew_rate gpio_get_slew_rate(uint gpio) { // Enum encoding should match hardware encoding on RP2040 static_assert(PADS_BANK0_GPIO0_DRIVE_VALUE_8MA == GPIO_DRIVE_STRENGTH_8MA, ""); void gpio_set_drive_strength(uint gpio, enum gpio_drive_strength drive) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); hw_write_masked(&padsbank0_hw->io[gpio], (uint)drive << PADS_BANK0_GPIO0_DRIVE_LSB, PADS_BANK0_GPIO0_DRIVE_BITS @@ -132,25 +134,28 @@ void gpio_set_drive_strength(uint gpio, enum gpio_drive_strength drive) { } enum gpio_drive_strength gpio_get_drive_strength(uint gpio) { - invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); + check_gpio_param(gpio); return (enum gpio_drive_strength)((padsbank0_hw->io[gpio] & PADS_BANK0_GPIO0_DRIVE_BITS) >> PADS_BANK0_GPIO0_DRIVE_LSB); } -static void gpio_irq_handler(void) { - io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ? - &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl; - for (uint gpio = 0; gpio < NUM_BANK0_GPIOS; gpio++) { - io_ro_32 *status_reg = &irq_ctrl_base->ints[gpio / 8]; - uint events = (*status_reg >> 4 * (gpio % 8)) & 0xf; - if (events) { - // TODO: If both cores care about this event then the second core won't get the irq? - gpio_acknowledge_irq(gpio, events); - gpio_irq_callback_t callback = _callbacks[get_core_num()]; - if (callback) { - callback(gpio, events); +static void gpio_default_irq_handler(void) { + uint core = get_core_num(); + gpio_irq_callback_t callback = callbacks[core]; + io_irq_ctrl_hw_t *irq_ctrl_base = core ? &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl; + for (uint gpio = 0; gpio < NUM_BANK0_GPIOS; gpio+=8) { + uint32_t events8 = irq_ctrl_base->ints[gpio >> 3u]; + // note we assume events8 is 0 for non-existent GPIO + for(uint i=gpio;events8 && i>= 4; } } } @@ -178,21 +183,48 @@ void gpio_set_irq_enabled(uint gpio, uint32_t events, bool enabled) { void gpio_set_irq_enabled_with_callback(uint gpio, uint32_t events, bool enabled, gpio_irq_callback_t callback) { gpio_set_irq_enabled(gpio, events, enabled); + gpio_set_irq_callback(callback); + if (enabled) irq_set_enabled(IO_IRQ_BANK0, true); +} - // TODO: Do we want to support a callback per GPIO pin? - // Install IRQ handler - _callbacks[get_core_num()] = callback; - irq_set_exclusive_handler(IO_IRQ_BANK0, gpio_irq_handler); - irq_set_enabled(IO_IRQ_BANK0, true); +void gpio_set_irq_callback(gpio_irq_callback_t callback) { + uint core = get_core_num(); + if (callbacks[core]) { + if (!callback) { + irq_remove_handler(IO_IRQ_BANK0, gpio_default_irq_handler); + } + callbacks[core] = callback; + } else if (callback) { + callbacks[core] = callback; + irq_add_shared_handler(IO_IRQ_BANK0, gpio_default_irq_handler, GPIO_IRQ_CALLBACK_ORDER_PRIORITY); + } +} + +void gpio_add_raw_irq_handler_with_order_priority_masked(uint gpio_mask, irq_handler_t handler, uint8_t order_priority) { + hard_assert(!(raw_irq_mask[get_core_num()] & gpio_mask)); // should not add multiple handlers for the same event + raw_irq_mask[get_core_num()] |= gpio_mask; + irq_add_shared_handler(IO_IRQ_BANK0, handler, order_priority); +} + +void gpio_add_raw_irq_handler_masked(uint gpio_mask, irq_handler_t handler) { + gpio_add_raw_irq_handler_with_order_priority_masked(gpio_mask, handler, GPIO_RAW_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); +} + +void gpio_remove_raw_irq_handler_masked(uint gpio_mask, irq_handler_t handler) { + assert(raw_irq_mask[get_core_num()] & gpio_mask); // should not remove handlers that are not added + irq_remove_handler(IO_IRQ_BANK0, handler); + raw_irq_mask[get_core_num()] &= ~gpio_mask; } void gpio_set_dormant_irq_enabled(uint gpio, uint32_t events, bool enabled) { + check_gpio_param(gpio); io_irq_ctrl_hw_t *irq_ctrl_base = &iobank0_hw->dormant_wake_irq_ctrl; _gpio_set_irq_enabled(gpio, events, enabled, irq_ctrl_base); } void gpio_acknowledge_irq(uint gpio, uint32_t events) { - iobank0_hw->intr[gpio / 8] = events << 4 * (gpio % 8); + check_gpio_param(gpio); + iobank0_hw->intr[gpio / 8] = events << (4 * (gpio % 8)); } #define DEBUG_PIN_MASK (((1u << PICO_DEBUG_PIN_COUNT)-1) << PICO_DEBUG_PIN_BASE) @@ -222,7 +254,7 @@ void gpio_deinit(uint gpio) { } void gpio_init_mask(uint gpio_mask) { - for(uint i=0;i<32;i++) { + for(uint i=0;i= NUM_BANK0_GPIOS); +} + // ---------------------------------------------------------------------------- // Pad Controls + IO Muxing // ---------------------------------------------------------------------------- @@ -347,63 +353,288 @@ void gpio_set_drive_strength(uint gpio, enum gpio_drive_strength drive); */ enum gpio_drive_strength gpio_get_drive_strength(uint gpio); -/*! \brief Enable or disable interrupts for specified GPIO +/*! \brief Enable or disable specific interrupt events for specified GPIO * \ingroup hardware_gpio * - * \note The IO IRQs are independent per-processor. This configures IRQs for + * This function sets which GPIO events cause a GPIO interrupt on the calling core. See + * \ref gpio_set_irq_callback, \ref gpio_set_irq_enabled_with_callback and + * \ref gpio_add_raw_irq_handler to set up a GPIO interrupt handler to handle the events. + * + * \note The IO IRQs are independent per-processor. This configures the interrupt events for * the processor that calls the function. * * \param gpio GPIO number - * \param events Which events will cause an interrupt + * \param event_mask Which events will cause an interrupt * \param enabled Enable or disable flag * - * Events is a bitmask of the following: + * Events is a bitmask of the following \ref gpio_irq_level values: * - * bit | interrupt - * ----|---------- - * 0 | Low level - * 1 | High level - * 2 | Edge low - * 3 | Edge high + * bit | constant | interrupt + * ----|---------------------------------------------------------- + * 0 | GPIO_IRQ_LEVEL_LOW | Continuously while level is low + * 1 | GPIO_IRQ_LEVEL_HIGH | Continuously while level is high + * 2 | GPIO_IRQ_EDGE_FALL | On each transition from high to low + * 3 | GPIO_IRQ_EDGE_RISE | On each transition from low to high + * + * which are specified in \ref gpio_irq_level */ -void gpio_set_irq_enabled(uint gpio, uint32_t events, bool enabled); +void gpio_set_irq_enabled(uint gpio, uint32_t event_mask, bool enabled); -/*! \brief Enable interrupts for specified GPIO +// PICO_CONFIG: GPIO_IRQ_CALLBACK_ORDER_PRIORITY, the irq priority order of the default IRQ callback, min=0, max=255, default=PICO_SHARED_IRQ_HANDLER_LOWEST_ORDER_PRIORITY, group=hardware_gpio +#ifndef GPIO_IRQ_CALLBACK_ORDER_PRIORITY +#define GPIO_IRQ_CALLBACK_ORDER_PRIORITY PICO_SHARED_IRQ_HANDLER_LOWEST_ORDER_PRIORITY +#endif + +// PICO_CONFIG: GPIO_RAW_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY, the irq priority order of raw IRQ handlers if the priortiy is not specified, min=0, max=255, default=PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY, group=hardware_gpio +#ifndef GPIO_RAW_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY +#define GPIO_RAW_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY +#endif + +/*! \brief Set the generic callback used for GPIO IRQ events for the current core * \ingroup hardware_gpio * - * \note The IO IRQs are independent per-processor. This configures IRQs for + * This function sets the callback used for all GPIO IRQs on the current core that are not explicitly + * hooked via \ref gpio_add_raw_irq_handler or other gpio_add_raw_irq_handler_ functions. + * + * This function is called with the GPIO number and event mask for each of the (not explicitly hooked) + * GPIOs that have events enabled and that are pending (see \ref gpio_get_irq_event_mask). + * + * \note The IO IRQs are independent per-processor. This function affects * the processor that calls the function. * - * \param gpio GPIO number - * \param events Which events will cause an interrupt. See \ref gpio_set_irq_enabled for details. - * \param enabled Enable or disable flag - * \param callback user function to call on GPIO irq. Note only one of these can be set per processor. - * - * \note Currently the GPIO parameter is ignored, and this callback will be called for any enabled GPIO IRQ on any pin. - * + * \param callback default user function to call on GPIO irq. Note only one of these can be set per processor. */ -void gpio_set_irq_enabled_with_callback(uint gpio, uint32_t events, bool enabled, gpio_irq_callback_t callback); +void gpio_set_irq_callback(gpio_irq_callback_t callback); -/*! \brief Enable dormant wake up interrupt for specified GPIO +/*! \brief Convenience function which performs multiple GPIO IRQ related initializations + * \ingroup hardware_gpio + * + * This method is a slightly eclectic mix of initialization, that: + * + * \li Updates whether the specified events for the specified GPIO causes an interrupt on the calling core based + * on the enable flag. + * + * \li Sets the callback handler for the calling core to callback (or clears the handler if the callback is NULL). + * + * \li Enables GPIO IRQs on the current core if enabled is true. + * + * This method is commonly used to perform a one time setup, and following that any additional IRQs/events are enabled + * via \ref gpio_set_irq_enabled. All GPIOs/events added in this way on the same core share the same callback; for multiple + * independent handlers for different GPIOs you should use \ref gpio_add_raw_irq_handler and related functions. + * + * This method is equivalent to: + * + * \code{.c} + * gpio_set_irq_enabled(gpio, event_mask, enabled); + * gpio_set_irq_callback(callback); + * if (enabled) irq_set_enabled(IO_IRQ_BANK0, true); + * \endcode + * + * \note The IO IRQs are independent per-processor. This method affects only the processor that calls the function. + * + * \param gpio GPIO number + * \param event_mask Which events will cause an interrupt. See \ref gpio_irq_level for details. + * \param enabled Enable or disable flag + * \param callback user function to call on GPIO irq. if NULL, the callback is removed + */ +void gpio_set_irq_enabled_with_callback(uint gpio, uint32_t event_mask, bool enabled, gpio_irq_callback_t callback); + +/*! \brief Enable dormant wake up interrupt for specified GPIO and events * \ingroup hardware_gpio * * This configures IRQs to restart the XOSC or ROSC when they are * disabled in dormant mode * * \param gpio GPIO number - * \param events Which events will cause an interrupt. See \ref gpio_set_irq_enabled for details. + * \param event_mask Which events will cause an interrupt. See \ref gpio_irq_level for details. * \param enabled Enable/disable flag */ -void gpio_set_dormant_irq_enabled(uint gpio, uint32_t events, bool enabled); +void gpio_set_dormant_irq_enabled(uint gpio, uint32_t event_mask, bool enabled); -/*! \brief Acknowledge a GPIO interrupt +/*! \brief Return the current interrupt status (pending events) for the given GPIO * \ingroup hardware_gpio * * \param gpio GPIO number - * \param events Bitmask of events to clear. See \ref gpio_set_irq_enabled for details. - * + * \return Bitmask of events that are currently pending for the GPIO. See \ref gpio_irq_level for details. + * \sa gpio_acknowledge_irq */ -void gpio_acknowledge_irq(uint gpio, uint32_t events); +static inline uint32_t gpio_get_irq_event_mask(uint gpio) { + check_gpio_param(gpio); + io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ? + &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl; + io_ro_32 *status_reg = &irq_ctrl_base->ints[gpio >> 3u]; + return (*status_reg >> (4 * (gpio & 7u))) & 0xfu; +} + +/*! \brief Acknowledge a GPIO interrupt for the specified events on the calling core + * \ingroup hardware_gpio + * + * \note This may be called with a mask of any of valid bits specified in \ref gpio_irq_level, however + * it has no effect on \a level sensitive interrupts which remain pending while the GPIO is at the specified + * level. When handling \a level sensitive interrupts, you should generally disable the interrupt (see + * \ref gpio_set_irq_enabled) and then set it up again later once the GPIO level has changed (or to catch + * the opposite level). + * + * \param gpio GPIO number + * \param events Bitmask of events to clear. See \ref gpio_set_irq_enabled for details. + * + * \note For callbacks set with \ref gpio_set_irq_enabled_with_callback, or \ref gpio_set_irq_callback,this function is called automatically. + * \param event_mask Bitmask of events to clear. See \ref gpio_irq_level for details. + */ +void gpio_acknowledge_irq(uint gpio, uint32_t event_mask); + +/*! \brief Adds a raw GPIO IRQ handler for the specified GPIOs on the current core + * \ingroup hardware_gpio + * + * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback), + * it is possible to add explicit GPIO IRQ handlers which are called independent of the default callback. The order + * relative to the default callback can be controlled via the order_priority parameter (the default callback has the priority + * \ref GPIO_IRQ_CALLBACK_ORDER_PRIORITY which defaults to the lowest priority with the intention of it running last). + * + * This method adds such an explicit GPIO IRQ handler, and disables the "default" callback for the specified GPIOs. + * + * \note Multiple raw handlers should not be added for the same GPIOs, and this method will assert if you attempt to. + * + * A raw handler should check for whichever GPIOs and events it handles, and acknowledge them itself; it might look something like: + * + * \code{.c} + * void my_irq_handler(void) { + * if (gpio_get_irq_event_mask(my_gpio_num) & my_gpio_event_mask) { + * gpio_acknowledge_irq(my_gpio_num, my_gpio_event_mask); + * // handle the IRQ + * } + * if (gpio_get_irq_event_mask(my_gpio_num2) & my_gpio_event_mask2) { + * gpio_acknowledge_irq(my_gpio_num2, my_gpio_event_mask2); + * // handle the IRQ + * } + * } + * \endcode + * + * @param gpio_mask a bit mask of the GPIO numbers that will no longer be passed to the default callback for this core + * @param handler the handler to add to the list of GPIO IRQ handlers for this core + * @param order_priority the priority order to determine the relative position of the handler in the list of GPIO IRQ handlers for this core. + */ +void gpio_add_raw_irq_handler_with_order_priority_masked(uint gpio_mask, irq_handler_t handler, uint8_t order_priority); + +/*! \brief Adds a raw GPIO IRQ handler for a specific GPIO on the current core + * \ingroup hardware_gpio + * + * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback), + * it is possible to add explicit GPIO IRQ handlers which are called independent of the default callback. The order + * relative to the default callback can be controlled via the order_priority parameter(the default callback has the priority + * \ref GPIO_IRQ_CALLBACK_ORDER_PRIORITY which defaults to the lowest priority with the intention of it running last). + * + * This method adds such a callback, and disables the "default" callback for the specified GPIO. + * + * \note Multiple raw handlers should not be added for the same GPIO, and this method will assert if you attempt to. + * + * A raw handler should check for whichever GPIOs and events it handles, and acknowledge them itself; it might look something like: + * + * \code{.c} + * void my_irq_handler(void) { + * if (gpio_get_irq_event_mask(my_gpio_num) & my_gpio_event_mask) { + * gpio_acknowledge_irq(my_gpio_num, my_gpio_event_mask); + * // handle the IRQ + * } + * } + * \endcode + * + * @param gpio the GPIO number that will no longer be passed to the default callback for this core + * @param handler the handler to add to the list of GPIO IRQ handlers for this core + * @param order_priority the priority order to determine the relative position of the handler in the list of GPIO IRQ handlers for this core. + */ +static inline void gpio_add_raw_irq_handler_with_order_priority(uint gpio, irq_handler_t handler, uint8_t order_priority) { + check_gpio_param(gpio); + gpio_add_raw_irq_handler_with_order_priority_masked(1u << gpio, handler, order_priority); +} + +/*! \brief Adds a raw GPIO IRQ handler for the specified GPIOs on the current core + * \ingroup hardware_gpio + * + * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback), + * it is possible to add explicit GPIO IRQ handlers which are called independent of the default event callback. + * + * This method adds such a callback, and disables the "default" callback for the specified GPIOs. + * + * \note Multiple raw handlers should not be added for the same GPIOs, and this method will assert if you attempt to. + * + * A raw handler should check for whichever GPIOs and events it handles, and acknowledge them itself; it might look something like: + * + * \code{.c} + * void my_irq_handler(void) { + * if (gpio_get_irq_event_mask(my_gpio_num) & my_gpio_event_mask) { + * gpio_acknowledge_irq(my_gpio_num, my_gpio_event_mask); + * // handle the IRQ + * } + * if (gpio_get_irq_event_mask(my_gpio_num2) & my_gpio_event_mask2) { + * gpio_acknowledge_irq(my_gpio_num2, my_gpio_event_mask2); + * // handle the IRQ + * } + * } + * \endcode + * + * @param gpio_mask a bit mask of the GPIO numbers that will no longer be passed to the default callback for this core + * @param handler the handler to add to the list of GPIO IRQ handlers for this core + */ +void gpio_add_raw_irq_handler_masked(uint gpio_mask, irq_handler_t handler); + +/*! \brief Adds a raw GPIO IRQ handler for a specific GPIO on the current core + * \ingroup hardware_gpio + * + * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback), + * it is possible to add explicit GPIO IRQ handlers which are called independent of the default event callback. + * + * This method adds such a callback, and disables the "default" callback for the specified GPIO. + * + * \note Multiple raw handlers should not be added for the same GPIO, and this method will assert if you attempt to. + * + * A raw handler should check for whichever GPIOs and events it handles, and acknowledge them itself; it might look something like: + * + * \code{.c} + * void my_irq_handler(void) { + * if (gpio_get_irq_event_mask(my_gpio_num) & my_gpio_event_mask) { + * gpio_acknowledge_irq(my_gpio_num, my_gpio_event_mask); + * // handle the IRQ + * } + * } + * \endcode + * + * @param gpio the GPIO number that will no longer be passed to the default callback for this core + * @param handler the handler to add to the list of GPIO IRQ handlers for this core + */ +static inline void gpio_add_raw_irq_handler(uint gpio, irq_handler_t handler) { + check_gpio_param(gpio); + gpio_add_raw_irq_handler_masked(1u << gpio, handler); +} + +/*! \brief Removes a raw GPIO IRQ handler for the specified GPIOs on the current core + * \ingroup hardware_gpio + * + * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback), + * it is possible to add explicit GPIO IRQ handlers which are called independent of the default event callback. + * + * This method removes such a callback, and enables the "default" callback for the specified GPIOs. + * + * @param gpio_mask a bit mask of the GPIO numbers that will now be passed to the default callback for this core + * @param handler the handler to remove from the list of GPIO IRQ handlers for this core + */ +void gpio_remove_raw_irq_handler_masked(uint gpio_mask, irq_handler_t handler); + +/*! \brief Removes a raw GPIO IRQ handler for the specified GPIO on the current core + * \ingroup hardware_gpio + * + * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback), + * it is possible to add explicit GPIO IRQ handlers which are called independent of the default event callback. + * + * This method removes such a callback, and enables the "default" callback for the specified GPIO. + * + * @param gpio the GPIO number that will now be passed to the default callback for this core + * @param handler the handler to remove from the list of GPIO IRQ handlers for this core + */ +static inline void gpio_remove_raw_irq_handler(uint gpio, irq_handler_t handler) { + check_gpio_param(gpio); + gpio_remove_raw_irq_handler_masked(1u << gpio, handler); +} /*! \brief Initialise a GPIO for (enabled I/O and set func to GPIO_FUNC_SIO) * \ingroup hardware_gpio